views:

54

answers:

1

Hi,

I would like to bind a textbox to show the text of the item below the item that is selected.

Example:

Item 1 - Text = row numer one
Item 2 - Text = row number two
Item 3 - Text = row number three

I select item 2

output

Textbox 1 - Text = row number two (this is easily set up binding to selecteditem)
Textbox 2 - Text = row number three

I was thinking of a custom xpath of selectedindex + 1 but this doesn't seem to work

+1  A: 

first attempt failed -- see below

You need to implement an IValueConverter and set it to the Converter attribute of the binding.

Create a class that inherits from IValueConverter, and in the Convert method, you'll cast the value parameter to ListBox (because you'll be binding the TextBox to the ListBox itself and letting the converter turn that into something meaningful).

Then get a reference to the ListBox's SelectedIndex property.

You want to return listBox.Items[selectedIndex + 1] from the method.

You can leave the ConvertBack method unimplemented.

You'll also have to handle the case where the last item in the ListBox is selected, because index + 1 will be out of bounds. Maybe you want to return the first item; maybe you want to return null or string.Empty.

update: custom ListBox

As requested, here is a sample that uses a custom ListBox with an additional [Dependency] property called "ItemAfterSelected."

First, the code for the derived control:

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
     public class PlusOneListBox : ListBox
     {
          public PlusOneListBox()
          {
                SelectionMode = SelectionMode.Single;
          }

          public object ItemAfterSelected
          {
                get { return GetValue(ItemAfterSelectedProperty); }
                set { SetValue(ItemAfterSelectedProperty, value); }
          }
          public static readonly DependencyProperty ItemAfterSelectedProperty = DependencyProperty.Register(
                "ItemAfterSelected", typeof (object), typeof (PlusOneListBox));

          protected override void OnSelectionChanged(SelectionChangedEventArgs e)
          {
                var newly_selected = e.AddedItems;
                if (newly_selected == null) ItemAfterSelected = null;
                else
                {
                     var last_index = Items.Count - 1;
                     var index = Items.IndexOf(newly_selected[0]);
                     ItemAfterSelected = index < last_index
                                                     ? Items[index + 1]
                                                     : null;
                }
                base.OnSelectionChanged(e);
          }
     }
}

Here is a sample window that shows how to use and bind to the control (you can drop this in to an app and run it to see it in action).

<Window x:Class="WpfApplication1.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          xmlns:custom="clr-namespace:WpfApplication1" xmlns:System="clr-namespace:System;assembly=mscorlib" Padding="24">
     <StackPanel>
          <custom:PlusOneListBox x:Name="custom_listbox">
                <custom:PlusOneListBox.Items>
                     <System:String>one</System:String>
                     <System:String>two</System:String>
                     <System:String>three</System:String>
                     <System:String>four</System:String>
                     <System:String>five</System:String>
                     <System:String>six</System:String>
                </custom:PlusOneListBox.Items>
          </custom:PlusOneListBox>
          <StackPanel Orientation="Horizontal" Margin="8">
                <TextBlock Text="Selected: " />
                <TextBlock Text="{Binding SelectedItem, ElementName=custom_listbox}" />
          </StackPanel>
          <StackPanel Orientation="Horizontal" Margin="8">
                <TextBlock Text="Next: " />
                <TextBlock Text="{Binding ItemAfterSelected, ElementName=custom_listbox}" />
          </StackPanel>
     </StackPanel>
</Window>
Jay
Can you give me an example? I curently have the following but I know that I'm not even close to what it should be...public class IndexConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { listBox_Copy.SelectedIndex; return listBox_Copy[SelectedIndex + 1]; } public object ConvertBack(object listBox_Copy, Type targetType, object parameter, System.Globalization.CultureInfo culture){} }
internetmw
This solution won't work because the converter will only get called once when the ListBox is created. Because you're binding to the ListBox itself the Binding won't be getting any change notifications when the selection changes so won't try to update and call Convert again.
John Bowen
So what do you suggest?
internetmw
@John Ah, you are right, of course. At this point, I'd suggest creating a derived `ListBox` and just add a `DependencyProperty` like `ItemAfterSelected` which gets updated whenever the `SelectedItem` changes (override `OnSelectionChanged`).
Jay
Can you provide me with some example code, I've got no clue how to implement this..
internetmw
@interetwjm I'll work up a quick sample this evening (GMT -5) and post the code in an update.
Jay
Hey, any chance of giving me an example? That would be great
internetmw
@internetwjm Oooo! Sorry about that -- yes; forthcoming!
Jay
@internetwjm Updated with sample.
Jay