В WPF как изменить привязку текста текстового блока DataTemplate в коде?

У меня есть ListBox, ItemsSource которого привязан к списку объектов. Listbox имеет ItemTemplate с DataTemplate, содержащим TextBlock. Текст текстового блока привязан к свойству Name объекта (т.е. Text = "{Binding Name}").

Я хотел бы установить переключатель, чтобы отображать разные представления одного и того же списка. Например, разрешите пользователю переключаться между свойством Name и свойством ID.

Я нашел SO-ответ на этот вопрос в 2381740, но у меня также есть граница и стиль текстового поля, установленный в шаблоне данных (см. код ниже).

Есть ли способ просто сбросить привязку текстового блока? Я не хочу воссоздавать всю таблицу данных. На самом деле я даже не уверен, как это сделать, есть ли простой способ перевести xaml в код ?.

Спасибо Коди

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

person code    schedule 29.06.2010    source источник


Ответы (3)


Просто упростите для себя: используйте два текстовых блока и скройте один из них.

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>

Код позади:

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"};
            }
        }
    }
}
person Wallstreet Programmer    schedule 29.06.2010
comment
Это гораздо лучшее решение, тем более что оно поддерживает отображение обоих. Спасибо! - person code; 29.06.2010
comment
Да, это очень хорошее решение, учитывая необходимость привязки двух значений к переключателям. (+1) Вас также может заинтересовать добавленное мною более общее решение. - person Ray Burns; 30.06.2010

Решение Wallstreet Programmer подходит вам, потому что вы используете переключатели. Однако есть более общее решение, о котором я подумал, что следует упомянуть для будущих читателей этого вопроса.

Вы можете изменить свой DataTemplate, чтобы использовать простой "{Binding}"

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

Тогда в коде вам не нужно воссоздавать полный DataTemplate. Все, что вам нужно сделать, это воссоздать это:

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

что легко:

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 }; 
} 

Это особенно удобно, если у вас много свойств, даже в примере с переключателем.

person Ray Burns    schedule 30.06.2010

Вы также можете использовать преобразователь значений, чтобы выбрать любое свойство вашего объекта данных. Вам нужно будет выполнить привязку ко всему объекту, а не к отдельным свойствам. Если ваш объект данных реализует INotifyPropertyChanged, тогда это решение не сработает для вас.

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>

код позади:

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
    }
}
person Wallstreet Programmer    schedule 29.06.2010