0

I want to have a "Custom" item inside a ListBox, so that the user can enter custom values apart from the preset selectable values.

Here's my XAML code:

        <ListBox SelectedValue="{Binding SomeValue}"
                 SelectedValuePath="Tag"
                 Style="{StaticResource HorizontalRadioButtonList}">
            <!-- style from http://stackoverflow.com/a/4120034/1813487 -->
            <ListBoxItem Tag="10" 
                     Content="10"/>
            <ListBoxItem Tag="20" 
                     Content="20"/>
            <ListBoxItem Tag="30" 
                     Content="30"/>
            <ListBoxItem x:Name="CustomListBoxItem">
                <TextBox Text="{Binding ElementName=CustomListBoxItem, 
                                        Mode=OneWayToSource, 
                                        UpdateSourceTrigger=PropertyChanged, 
                                        Path=Tag}"/>
            </ListBoxItem>
        </ListBox>
        <TextBlock Text="{Binding SomeValue}"/>

How to make SomeValue update as soon as the user enters something in the TextBox? Currently the ListBox does not detect that the Tag changed, and does not update SomeValue.

marczellm
  • 1,224
  • 2
  • 18
  • 42
  • @MarcelB That makes no sense. SomeValue is a property of my DataContext. UniqueValue is the name of the last ListBoxItem. Otherwise, `{Binding UniqueValue.Tag}`, if it works, is equivalent to what I have there. – marczellm Jul 31 '14 at 08:40
  • yeah, you’re right. I mixed some things up there … sorry. – Marcel B Jul 31 '14 at 08:49

2 Answers2

0

I think you can achieve that using a behavior attached to the TextBox, where you sign the TextChanged event and everytime it is triggered you could set the ListBoxItem's tag to the TextBox's text.

I did something similar to that, but using Microsoft.Expression.Interactivity:

<ListBoxItem x:Name="UniqueValue" Tag="40">
    <TextBox x:Name="TextBox" Text="40">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="TextChanged">
                <ei:ChangePropertyAction PropertyName="Tag"
                                                             TargetObject="{Binding ElementName=UniqueValue}"
                                                             Value="{Binding Text,
                                                                             ElementName=TextBox}" />

                <ei:ChangePropertyAction PropertyName="SomeValue"
                                                             TargetObject="{Binding}"
                                                             Value="{Binding Text,
                                                                             ElementName=TextBox,
                                                                             Converter={StaticResource StringToIntConverter}}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
</ListBoxItem>

Basically, everytime the text is changed, the property Tag will have its value set to the written text, and after that (to keep the selection of the ListBoxItem) the viewmodel's property SomeValue has its value set the same text.

Namespaces:

    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

Assemblies required:

System.Windows.Interactivity
Microsoft.Expression.Interactions
MauricioW
  • 51
  • 4
  • Is this basically equivalent to writing an event handler for `TextChanged` and doing the same things in C# code? – marczellm Jul 31 '14 at 12:58
  • yes, but this way there's no need of code behind. Like i said, you can still create a behavior to do that. – MauricioW Jul 31 '14 at 13:04
  • My problem with this approach is that by setting the property on the ViewModel directly, it bypasses the binding, so all converters, validators etc. that were set on the original SomeValue binding do not take effect. – marczellm Jul 31 '14 at 14:08
  • ok i've assumed the SomeValue is an integer, so it requires the string tag to be converted before setting. I editted and put a converter before setting the SomeValue. – MauricioW Jul 31 '14 at 16:42
0

Here's how I did it, it's not the perfect solution either:

XAML:

<ListBox SelectedValue="{Binding SomeValue}"
             SelectedValuePath="Tag"
             Style="{StaticResource HorizontalRadioButtonList}"
             x:Name="CustomListBox">
        <!-- ...items... -->
        <ListBoxItem x:Name="CustomListBoxItem">
            <TextBox Text="{Binding ElementName=CustomListBoxItem, 
                                    Mode=OneWayToSource, 
                                    UpdateSourceTrigger=PropertyChanged, 
                                    Path=Tag}"
                     TextChanged="UpdateCustomValue"
                     x:Name="CustomTextBox"/>
        </ListBoxItem>
    </ListBox>
    <TextBlock Text="{Binding SomeValue}"/>

Code:

private void UpdateUniqueValue(object sender, TextChangedEventArgs e)
{
    var listBox = sender == CustomTextBox? CustomListBox: null;
    if (listBox != null)
    {
        var textBox = (TextBox) sender;
        try // so that invalid values are not allowed
        {
            listBox.SelectedValue = textBox.Text;
        }
        catch
        {
            textBox.Text = listBox.SelectedValue.ToString();
        }
    }
}

It is a workaround and does not take into account validators either, but at least does not allow invalid values to be entered.

marczellm
  • 1,224
  • 2
  • 18
  • 42