4
votes

I have a ListBox whose ItemsSource is bound to a list of objects. The Listbox has a ItemTemplate with a DataTemplate containing a TextBlock. The textblock's Text is bound to the object's Name property (i.e. Text="{Binding Name}").

I would like to provide a radio button to show different views of the same list. For example allow a user to toggle between the Name property and an ID property.

I found a SO answer for this at 2381740 but I also have border and a textbox style set in data template (see code below).

Is there anyway to just reset the Textblock binding? I don't want to have to recreate the entire datatemplate. Actually I'm not even sure how to do that, is there an easy way to translating xaml to code?.

Thanks Cody

<DataTemplate>
  <Border Margin="0 0 2 2"
          BorderBrush="Black"
          BorderThickness="3"
          CornerRadius="4"
          Padding="3">
      <TextBlock Style="{StaticResource listBoxItemStyle}"
                 Text="{Binding Name}" />
  </Border>
</DataTemplate>
3

3 Answers

7
votes

Just make it simple for yourself and use two textblocks and hide one of them.

XAML:

<Window x:Class="Test.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="300" Width="300">

  <Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
  </Window.Resources>

  <StackPanel>
    <RadioButton Name="nameRadioBtn" Content="Name" IsChecked="True"/>
    <RadioButton Name="lengthRadioBtn" Content="Length" />
    <ListBox
      ItemsSource="{Binding Path=Items}">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Border BorderBrush="Red" BorderThickness="1">
            <Grid>
              <TextBlock 
                Text="{Binding .}" 
                Visibility="{Binding Path=IsChecked, ElementName=nameRadioBtn, 
                  Converter={StaticResource BooleanToVisibilityConverter}}" />
              <TextBlock 
                Text="{Binding Path=Length}" 
                Visibility="{Binding Path=IsChecked, ElementName=lengthRadioBtn,
                  Converter={StaticResource BooleanToVisibilityConverter}}" />
            </Grid>
          </Border>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </StackPanel>        
</Window>

Code behind:

using System.Collections.Generic;
using System.Windows;

namespace Test
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            DataContext = this;
        }

        public IEnumerable<string> Items
        {
            get
            {
                return new List<string>() {"Bob", "Sally", "Anna"};
            }
        }
    }
}
13
votes

Wallstreet Programmer's solution works well for you because you are using radio buttons. However there is a more general solution that I thought I should mention for future readers of this question.

You can change your DataTemplate to use plain "{Binding}"

<DataTemplate x:Key="ItemDisplayTemplate">
  <Border ...> 
    <TextBlock ...
               Text="{Binding}" /> 
  </Border> 
</DataTemplate> 

Then in code you don't have to recreate a full DataTemplate. All you have to do is recreate this:

<DataTemplate>
  <ContentPresenter Content="{Binding Name}" ContentTemplate="{StaticResource ItemDisplayTemplate}" />
</DataTemplate>

which is easy:

private DataTemplate GeneratePropertyBoundTemplate(string property, string templateKey)
{
  var template = FindResource(templateKey);
  FrameworkElementFactory factory = new FrameworkElementFactory(typeof(ContentPresenter)); 
  factory.SetValue(ContentPresenter.ContentTemplateProperty, template);
  factory.SetBinding(ContentPresenter.ContentProperty, new Binding(property)); 
  return new DataTemplate { VisualTree = factory }; 
} 

This is particularly convenient if you have many properties, even in your radio button example.

2
votes

You can also use a value converter to pick any property of your data object. You will need to bind to the whole object instead of individual properties. If your data object implements INotifyPropertyChanged then this solution will not work for you.

XAML

<Window x:Class="Test.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Test="clr-namespace:Test"
    Height="300" Width="300">

    <Window.Resources>
        <Test:PropertyPickerConverter x:Key="PropertyPickerConverter" />
    </Window.Resources>

    <StackPanel>
        <RadioButton Content="Name" Click="OnRadioButtonClick" IsChecked="True"/>
        <RadioButton Content="Length" Click="OnRadioButtonClick" />
        <ListBox
            ItemsSource="{Binding Path=Items}"
            Name="_listBox">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Red" BorderThickness="1">
                        <StackPanel>
                            <TextBlock 
                                Text="{Binding ., Converter={StaticResource PropertyPickerConverter}}" />
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>

</Window>

code behind:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace Test
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            _propertyPickerConverter = FindResource("PropertyPickerConverter") as PropertyPickerConverter;
            _propertyPickerConverter.PropertyName = "Name";

            DataContext = this;
        }

        public IEnumerable<string> Items
        {
            get
            {
                return new List<string>() {"Bob", "Sally", "Anna"};
            }
        }

        private void OnRadioButtonClick(object sender, RoutedEventArgs e)
        {
            _propertyPickerConverter.PropertyName = (sender as RadioButton).Content as string;

            _listBox.Items.Refresh();
        }

        private PropertyPickerConverter _propertyPickerConverter;
    }

    public class PropertyPickerConverter : IValueConverter
    {
        public string PropertyName { get; set; }

        #region IValueConverter Members
        public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string item = value as string;
            switch (PropertyName)
            {
                case "Name": return item;
                case "Length": return item.Length;
                default: return null;
            }
        }

        public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new System.NotImplementedException();
        }
        #endregion
    }
}