Let WPF Tabcontrol height assume height of largest item? - wpf

Is there any way to have to tabcontrol take the size of the largest tab item (well, actually, the tabitem's content)?
Since the tabcontrol has no specific size assigned it should autosize: it does that correctly, but when you switch tabs it automatically resizes itself to the height (and width) of the contents of the currently selected tab.
I don't want the resizing to happen, and let the tabcontrol assume the height of the largest item, but still have it autosize itself.
Any clues? I tried databinding to the Height property of each element used as content to the using a multibinding, with bindings on both the ActualHeight and the Items properties of the Tabcontrol. But alas, the ActualHeight of the content elements is always 0.
<TabItem Header="Core" >
<Grid Margin="5">
<Grid.Height>
<MultiBinding Converter="{Converters1:AllTabContentEqualHeightConverter}">
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}"/>
<Binding Path="Items" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}"/>
</MultiBinding>
</Grid.Height>
...
Can this be done?

Yes it can be done: reuse-grid-rowdefinitions-for-each-tabitem
Example:
<TabControl Grid.IsSharedSizeScope="True">
<TabItem Header="Tab 1">
<Grid >
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="xxx"/>
</Grid.RowDefinitions>
</Grid>
</TabItem>
<TabItem Header="Tab 2">
<Grid >
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="xxx"/>
</Grid.RowDefinitions>
</Grid>
</TabItem>
</TabControl>

The problem is that the TabControl unloads and reloads its content as you switch tabs. Therefore it only knows about the size of the content in the currently active tab. You should be able to change the TabControl such that it never destroys its children, and they are always present (but maybe hidden).
This blog post by Eric Burke should get you started. From what I can tell by skimming his post, you will need to change it such that:
All children are loaded when the TabControl is loaded.
Children are hidden rather than collapsed when they are inactive

Actually, it was easier to solve that I thought.
Since I had a controltemplate for the TabControl anyway, I set the height of the ContentPresenter presenting the selected tab content. I do this using a converter that binds to the items of the TabControl, measures them if necessary (using Measure) and checks DesiredSize for the size I need.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var items = value as ItemCollection;
if (items == null)
return null;
double max = 0;
foreach (TabItem item in items)
{
var content = item.Content as FrameworkElement;
if (content == null) continue;
if (!content.IsMeasureValid)
content.Measure(new Size(int.MaxValue, int.MaxValue));
var height = content.DesiredSize.Height;
if (max < height)
max = height;
}
return max;
}
That works just fine, with some caveats:
every tab content should be a FrameworkElement
the contents don't change size once they are loaded (because the converter is only called when the Items property changes, ie just once).

This worked for me in conjunction with Grid.IsSharedSizeScope approach shown above.
Note that SetCurrentValue is used instead of just setting the SelectedIndex property - this way we keep possible existing bindings:
private void TabControl_OnLoaded(object sender, RoutedEventArgs e)
{
//NOTE: loop through tab items to force measurement and size the tab control to the largest tab
TabControl tabControl = (TabControl)sender;
// backup selection
int indexItemLast = tabControl.SelectedIndex;
int itemCount = tabControl.Items.Count;
for (
int indexItem = (itemCount - 1);
indexItem >= 0;
indexItem--)
{
tabControl.SetCurrentValue(Selector.SelectedIndexProperty, indexItem);
tabControl.UpdateLayout();
}
// restore selection
tabControl.SetCurrentValue(Selector.SelectedIndexProperty, indexItemLast);
}

It's probably not in the proper WPF way, but, if you already have all the content elements, you could maybe loop through them on load and set the height of the TabControl programatically.

Related

WPF ComboBox initially displays first item at wrong size

I have a ComboBox on a WPF window that is giving me some heartache, in that when first displayed, the first selection is rendered improperly. The ComboBox does not display text only; it displays an object of a type that descends from UserControl. Here's the XAML for the ComboBox itself:
<ComboBox Grid.Column="0"
Height="69"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
ItemsSource="{Binding Path=ViewChoices,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Mode=OneWay}"
Margin="10"
Name="ViewPicker"
SelectionChanged="ViewPicker_SelectionChanged"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center" />
As you can see, the ComboBox's ItemsSource is bound to a property of the UserControl that owns it called ViewChoices. The ViewChoices object is an ObservableCollection.
The contents of the ComboBox is set in code in the code behind, as the exact contents in the final code will be read from an XML file; the values are hard coded right now. Essentially, a CameraViewChoice object is created for each XML row read and added to the ViewChoices collection. This all happens in the UserControl's default constructor, after called InitializeComponent(). In the UserControl's Loaded event handler, I have code which sets the ComboBox's SelectedIndex property to 0.
The CameraViewChoice object is descended from UserControl. Here's the Xaml for this control:
<UserControl x:Class="CarSystem.CustomControls.CameraViewChoice"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cs="clr-namespace:CarSystem.CustomControls"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50">
<Border BorderBrush="Black" BorderThickness="1">
<Grid>
<Image Opacity="0.35" Source="..." Stretch="Uniform" />
<Label Content="{Binding Path=Text}"
FontSize="18"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Border>
</UserControl>
Here's the code-behind for the CameraViewChoice object:
public partial class CameraViewChoice : UserControl {
public static readonly DependencyProperty AttachedCameraProperty = DependencyProperty.Register( "AttachedCamera", typeof( Camera ), typeof( CameraViewChoice ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.AffectsRender ) );
public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof( string ), typeof( CameraViewChoice ), new FrameworkPropertyMetadata( string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.AffectsRender ) );
public Camera AttachedCamera {
get { return (Camera) GetValue( AttachedCameraProperty ); }
set { SetValue( AttachedCameraProperty, value ); }
}
public string Text {
get { return (string) GetValue( TextProperty ); }
set { SetValue( TextProperty, value ); }
}
public CameraViewChoice() {
InitializeComponent();
}
This is all working fine but there's one problem. When the program starts running and the ComboBox displayed for the first time, the selected item is displayed all wrong. The label is blank and the CameraViewChoice control is displayed too large, so much so that the bottom of it is cut off. What I'm seeing is a blank CameraViewChoice object displayed without scaling to the ComboBox.
If I choose an item in the list, all of the choices in the list display properly and are sized properly & look fine after that, including the selected one. The problem is only when the window is first displayed.
Does anyone have any ideas about what's going on?
Tony
Edit:
I did some research on Google & MSDN Magazine and I found an article by Josh Smith about Data Templates. From there, I made the following changes to the XAML for my ComboBox:
<ComboBox Grid.Column="0"
Height="69"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
ItemsSource="{Binding Path=ViewChoices,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Mode=OneWay}"
Margin="10"
Name="ViewPicker"
SelectionChanged="ViewPicker_SelectionChanged"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<cs:CameraViewChoice Margin="10" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This is better as the items in the ComboBox do not change size, but the initial display is still too large. That is, it's getting cut off at the bottom. Further, the size of the selected item is consistently too large. So when you select one item in the list, it's displayed in the combobox partially clipped.
I want the choice displayed in the ComboBox with all of it fitting in side of it. Any suggestions for changes to the CombobBox's ItemTemplate?
In the Loaded event, provided you have at least 2 items, set the SelectedIndex to 1. After this, no matter how many items you have, call InvalidateMeasure and UpdateLayout on the ComboBox, then set the SelectedIndex to 0.
Here's what I think is happening.
You are using a standard ComboBox and dynamically adding UIElements to it. When the ComboBox is first displayed, there are no items, so it uses a default template. After you start adding UIElements to it, the renderer then performs it measuring and arranging. In essence, it's only learning what it should look like after the UIElements are created and inserted (but it still needed to know what to look like before that happened).
My suggestion would be to move from this development pattern to a more common methodology. Instead of creating UIElements on the fly, just create an ObservableCollection of CameraChoices (or whatever name would be appropriate). Typically this would be contained in a ViewModel.
Then instead of creating a UserControl and inserting it into the ItemsSource of the ComboBox, you'd be better served to create an ItemsTemplate (where you can use the UserControl) for the ComboBox. Alternatively, you can use a DataTemplate of the same type as the object in the ObservableCollection.
This will provide a more robust mechanism for displaying the list of items and provide you with a way to get to the raw data instead of having to deal with a UIElement when the SelectionChanged event is signaled.

Resizing a Panels based on the size of user control

I'm trying to find a way to resize a LayoutPanel (DevExpress) based on the size of the user control that it contains. The user control is exposed using a ContentControl. Here's the relevant Code
This is the Layout panel and the coresponding view:
<dxd:LayoutPanel Caption="Search Criteria" CaptionImage="Images/Icons/DetailView.png">
<ContentControl Name="myContentControl" Content="{Binding Path=ProjectsSearchVM}"/>
</dxd:LayoutPanel>
The ProjectSearchVM is a property of the MainWindowViewModel, which is the DataContext for the code above. This property returns an object of type ProjectsSearchViewModel that is replaced by its corresponding View (containing a userControl) using a Resource File:
<DataTemplate DataType="{x:Type vm:ProjectSearchViewModel}">
<vw:ProjectSearchView />
</DataTemplate>
The problem is that my view is higher than the original size of the Layout Pannel. I'd like to bind the panel's MinSize to the size of my view (or the ContentControl containing it).
I tried this, but it doesn't work:
<dxd:LayoutPanel Caption="Search Criteria" CaptionImage="Images/Icons/DetailView.png">
<dxd:LayoutPanel.MinSize>
<Binding ElementName="myContentControl" Path="Size"/>
</dxd:LayoutPanel.MinSize>
<ContentControl Name="myContentControl" Content="{Binding Path=ProjectsSearchVM}" />
</dxd:LayoutPanel>
I'm still very new to WPF, so I'm sure the solution is simple.
Can anyone enlighten me?
The question was answered on the DevExpress site at this link:
http://www.devexpress.com/Support/Center/Question/Details/Q448884
For flyout, it involves overriding the control in the container, for example, if it's in a Border, something like this:
public class AutoSizeContainer : Border {
protected override void OnInitialized(EventArgs e) {
base.OnInitialized(e);
BaseLayoutItem item = DockLayoutManager.GetLayoutItem(this);
Child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
AutoHideGroup.SetAutoHideSize(item, new Size(Child.DesiredSize.Width + 6, Child.DesiredSize.Height + 6));
}
}
Making such an object the root object of the LayoutPanel in an AutoHide group, makes the flyout sizing correct.
I'm not familiar with DevExpress's LayoutPanel, but most standard WPF panels (Grid, StackPanel) "auto size" to contain their children. Are you setting hard Height and Width properties for the LayoutPanel (or does it only contain a Size property and not Height and Width like standard WPF controls)?
If you are forced to use hard Height/Width values, a common way to bind them would be like this:
<dxd:LayoutPanel ... Height="{Binding Height, ElementName=myContentControl}" Width="{Binding Width, ElementName=myContentControl}">
<ContentControl x:Name=myContentControl ... />
</dxd:LayoutPanel>
Normally, if you bind Heights and Widths you bind to ActualHeight or ActualWidth, but those properties do not exist on ContentControl. (Height and Width are only suggested values, so you may find that if the above binding is needed and works for you, you may need to tweak the values slightly with a value converter to account for margins or padding).

Why Does ItemsControl Not Use My ItemTemplate?

I am able to use an ItemTemplate within an ItemsControl to render items in a specific format. However, if one of the items within the ItemsControl happens to be, say, a TextBox, that TextBox is rendered rather than an instance of the ItemsTemplate. From what I can tell, this is true for any FrameworkElement. Is this intended behavior for an ItemsControl, or am I doing something incorrectly?
An example:
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5">
<Rectangle Fill="Blue" Height="20" Width="20" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<sys:Object />
<TextBox />
<sys:Object />
<Rectangle Fill="Red" Height="20" Width="20" />
</ItemsControl.Items>
</ItemsControl>
I expected this to display four blue rectangles. I thought that any time an ItemTemplate has been defined each item in the collection is rendered as an instance of the template. However, in this case the following is rendered: a blue rectangle followed by a TextBox followed by a blue rectangle followed by a red rectangle.
The ItemsControl has a protected member IsItemItsOwnContainerOverride which is passed an object from the items collection and returns true if that object can be added directly to the items panel without a generated container (and thereby be templated).
The base implementation returns true for any object that derives from UIElement.
To get the behaviour you would expect you would need to inherit from ItemsControl and override this method and have it always return false. Unfortunately thats not the end of the matter. The default implementation of PrepareContainerForItemOverride still doesn't assign the ItemTemplate to the container if the item is a UIElement so you need to override this method as well:-
protected override bool IsItemItsOwnContainerOverride(object item)
{
return false;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
((ContentPresenter)element).ContentTemplate = ItemTemplate;
}
I'm just speculating here, but I would bet that it's behavior that lives inside of the ItemContainerGenerator. I'd bet that the ItemContainerGenerator looks at an item, and if it's a UIElement it says, "cool, the item container's been generated, I'll just return it" and if it's not, it says, "I'd better generate a container for this item. Where's the DataTemplate?"

Change the layout of a TreeView to looks like multiple ListBox [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to change the layout of a databound treeview from this:
To this:
And of course selection must works properly:
Do you have any ideas about how to do that. I've been trying to change the template but I can't find out a way to have this behavior. Maybe a component already exists...
Thanks for your help !
This is difficult. It seems to need a HierarchicalDataTemplate, but because the behavior you want requires multiple ItemsControls, it is not going to work as expected. I don't think there is a way to create a TreeView template in XAML that will do this. Your best bet is to create a custom items control of some sort. You will probably need to do the items binding in code, rather than in XAML, because without the HierarchicalDataTemplate the XAML has no way of understanding nested relationships.
That being said, if you are guaranteed to only have 2 levels of nesting (as in your example), you could do this easily with the following mark-up:
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<ListBox Name="Level1" Width="150" Height="150"
ItemsSource="{Binding Collection}"
ItemTemplate="{StaticResource ItemTemplate}"/>
<ListBox Name="Level2" Width="150" Height="150"
ItemsSource="{Binding ElementName=Level1, Path=SelectedValue.Children}"
ItemTemplate="{StaticResource ItemTemplate}"/>
<ListBox Name="Level3" Width="150" Height="150"
ItemsSource="{Binding ElementName=Level2, Path=SelectedValue.Children}"
ItemTemplate="{StaticResource ItemTemplate}"/>
</StackPanel>
Where Collection is your root items collection and there is a property on each item called Children containing the child collection.
But I think what you are asking for is an items control that can support any number of nested levels, not just 2. So in that case, I would do this in code-behind. The binding will be the same- that is, at each level, the ListBox should be bound to the parent level's items. But you will obviously need to iterate and create one ListBox for each nested level.
I finally find a way out, but like you say Charlie, it involves creating ListBox:
I create a new CustomControl which inherits Control (I couldn’t use neither Selector or TreeView because I wouldn’t have been able to manage the SelectedItem property from the derived class)
In the template of this CustomControl is an ItemsControl. This ItemsControl has its ItemTemplate property set to a DataTemplate containing a ListBox.
The CustomControl has a Depth property of type int. This property indicates the number of ListBox that should be generated.
The CustomControl automatically databound ListBoxes together: each ListBox’s ItemsSource property is databound to the SelectedItem’s children property of the previous ListBox in the visual tree.
The CustomControl has a SelectedItem property and a SelectionChanged event (like Selector-derived class).
I added an IsReallySelected attached property to the ListBoxItem which are generated. This enables to databing an IsSelected property of the ViewModel class behind the control with the IsSelected of the ListBoxItem. I had to create an attached property because its value is true when the ListBoxItem is selected AND the parent ListBox has IsSelectionActive set to true.
I blogged about this solution (with source code) on my blog.
Its too bad I didn't notice this question before you went to all that work. It is easy to restyle a TreeView to appear this way: The only code required is a single very simple attached property, "VisibleWhenCurrentOf".
The way to do it is to:
Style TreeViewItem to include a ListBox in its ControlTemplate outside the ItemsPresenter.
Control the visibility of the TreeViewItem template using "VisibleWhenCurrentOf", so that a given item is only visible inside the ItemsPresenter if it is the current item within the ListBox.
Restyling details
Here is the XAML for the relevant templates:
<ControlTemplate TargetType="TreeView">
<DockPanel>
<ListBox
ItemsSource="{TemplateBinding ItemsSource}"
IsSyncrhonizedWithCurrentItem="true"
Style="{DynamicResource BoxesTreeViewBoxStyle}"
ItemTemplate="{Binding HeaderTemplate}"
ItemTemplateSelector="{Binding HeaderTemplateSelector}" />
<ItemsPresenter />
</DockPanel>
</ControlTemplate>
<ControlTemplate TargetType="TreeViewItem">
<DockPanel
local:VisibilityHelper.VisibleWhenCurrentOf="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor,HeaderedItemsControl,2}">
<ListBox
ItemsSource="{TemplateBinding ItemsSource}"
IsSyncrhonizedWithCurrentItem="true"
Style="{DynamicResource BoxesTreeViewBoxStyle}"
ItemTemplate="{Binding HeaderTemplate}"
ItemTemplateSelector="{Binding HeaderTemplateSelector}" />
<ItemsPresenter />
</DockPanel>
</ControlTemplate>
These two templates are identical except for the conditional visibilty. The way this works is that the "+" in front of the tree item becomes a ListBox, and all items except the one selected in the ListBox are hidden.
Your BoxesTreeViewBoxStyle should set a margin around the ListBox so they will space correctly. You can actually simplify this further by putting the ListBox property values in the style, but I find it more convenient to set them in the ControlTemplate so I can restyle the ListBox without having to remember these settings.
Attached property
Here is the code for the VisibleWhenCurrentOf attached property:
public class VisibilityHelper : DependencyObject
{
// VisibleWhenCurrentOf
public static object GetVisibleWhenCurrentOf(DependencyObject obj) { return (object)obj.GetValue(VisibleWhenCurrentOfProperty); }
public static void SetVisibleWhenCurrentOf(DependencyObject obj, object value) { obj.SetValue(VisibleWhenCurrentOfProperty, value); }
public static readonly DependencyProperty VisibleWhenCurrentOfProperty = DependencyProperty.RegisterAttached("VisibleWhenCurrentOf", typeof(object), typeof(VisibilityHelper), new UIPropertyMetadata
{
PropertyChangedCallback = (sender, e) =>
{
var element = sender as FrameworkElement;
if(e.OldValue!=null)
{
var oldView = e.OldValue as ICollectionView ?? CollectionViewSource.GetDefaultView(e.OldValue);
oldView.CurrentChanged -= UpdateVisibilityBasedOnCurrentOf;
if(e.NewValue==null) element.DataContextChanged -= UpdateVisibilityBasedOnCurrentOf;
}
if(e.NewValue!=null)
{
var newView = e.NewValue as ICollectionView ?? CollectionViewSource.GetDefaultView(e.OldValue);
newView.CurrentChanged += UpdateVisibilityBasedOnCurrentOf;
if(e.OldValue==null) element.DataContextChanged += UpdateVisibilityBasedOnCurrentOf;
}
UpdateVisibilityBasedOnCurrentOf(sender);
}
});
static void UpdateVisibilityBasedOnCurrentOf(object sender, DependencyPropertyChangedEventArgs e) { UpdateVisibilityBasedOnCurrentOf(sender); }
static void UpdateVisibilityBasedOnCurrentOf(object sender, EventArgs e) { UpdateVisibilityBasedOnCurrentOf(sender); }
static void UpdateVisibilityBasedOnCurrentOf(object sender)
{
var element = sender as FrameworkElement;
var source = GetVisibleWhenCurrentOf(element);
var view = source==null ? null : source as ICollectionView ?? CollectionViewSource.GetDefaultView(source);
var visible = view==null || view.CurrentItem == element.DataContext;
element.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
}
}
There is nothing complex here: Any time DataContext or the view's Current changes, visibilty is recomputed. The PropertyChangedCallback simply sets event handlers to detect these conditions and the UpdateVisibiltyBasedOnCurrentOf handler recomputes visibility.
Advantages of this solution
Since this solution is a real TreeView:
You get all the selection handling functionality for free.
It works with any number of tree levels.
You can use all the features of HierarchicalDataTemplate, including HeaderTemplate and HeaderTemplateSelector
You can use different ItemsSource bindings at each level rather than every collection requiring a "Children" proerty
It is a lot less code than a custom control

Getting at the Listbox's ItemContainer when data binding

Is there a way to get at the ItemContaner of a selected item in a listbox? In Silverlight 2.0 Beta 1 I could, but the container is hidden in Beta 2 of Silverlight 2.0.
I'm trying to resize the listbox item when it is unselected to a specific size and when selected to a variable size. I also want to get the relative position of the selected item for animations. Growing to a variable size and getting the relative pasition is why i need to get to the listbox item.
I should clarify i'm not adding items to the listbox explicitly. I am using data binding in xaml and DataTemplates. What I have trouble accessing is the ItemContainer of the selected item's DataTemplate.
There is a way to obtain the Panel containing the item's UIElement and the mapping of items to UIElements. You have to inherit from ListBox (this actually works for any ItemsControl) and override PrepareContainerForItemOverride:
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var el = element as FrameworkElement;
if (el != null)
{
// here is the elements's panel:
_itemsHost = el.Parent as Panel;
// item is original item inserted in Items or ItemsSource
// we can save the mapping between items and FrameworElements:
_elementMapping[item] = el;
}
}
This is kind of hackish, but it works just fine.
If you are adding non-UI elements to the listbox (such as strings or non-UI data objects), then this is probably pretty difficult. However if you wrap your items in some sort of FrameworkElement-derived object before adding them to the listbox, you can use TransformToVisual to get the relative size and use Height and Width to set the size of the item.
In general you can wrap your objects in a ContentControl like the following. Instead of:
_ListBox.Items.Add(obj0);
_ListBox.Items.Add(obj1);
Do this:
_ListBox.Items.Add(new ContentControl { Content = obj0 });
_ListBox.Items.Add(new ContentControl { Content = obj1 });
Now when you get _ListBox.SelectedItem you can cast it to ContentControl and set the size and get the relative position. If you need the original object, simply get the value of the item's Content property.
It appears that you can use relative binding to get at the Item Container from the ItemTemplate.
<TextBlock YourTargetProperty="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBoxItem}}, Mode=OneWay, Path=YourSourceProperty}" />
I found this solution here.
Update for silverlight 5.
<ListBox ItemsSource="{Binding Properties}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" />
</DataTemplate>
</ListBox.ItemTemplate>
RelativeSource AncestorType is now supported, making this much easier.

Resources