2016-03-17 3 views
1

У меня есть следующий код XAML:Binding к списку <string> не обновляет соответствующие строки

<Window x:Class="WpfApplication1.MainWindow" 
     x:Name="window" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfApplication1" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="327" Width="213" 
     DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="*"/> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions>   
     <ListBox ItemsSource="{Binding Strings}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <TextBox Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged}" /> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
     <TextBox Grid.Row="1" Text="{Binding TheString}" /> 
     <Button Click="ButtonBase_OnClick" Grid.Row="2">Check strings</Button> 
    </Grid> 
</Window> 

Довольно простой. Теперь вот мой код позади:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     this.InitializeComponent(); 
    } 

    public List<string> Strings { get; } = new List<string> { "Hello world1", "Hello world2", "Hello world3" }; 

    public string TheString { get; set; } = "Helloo"; 

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
    { 
     MessageBox.Show(string.Join("\n", this.Strings) + $"\n\n{this.TheString}"); 
    } 
} 

Проблема заключается в том, если я обновлю строковые значения в Strings списке из пользовательского интерфейса, он никогда не обновляется при нажатии на кнопку. Что я делаю не так?
Я правильно привязываю ListboxItemsSource к списку Strings. Я также правильно привязываю TextBox в ItemTemplate.

+0

охвата, но добавить режим = TwoWay в TextBox – Paparazzi

+1

По умолчанию режима для TextBox является TwoWay, однако у вас нет сеттера для вашего списка. Если нет Setter, я думаю, что ваша коллекция будет доступна только для чтения, поэтому вы не сможете ее обновить. – Bearcat9425

+0

@ Bearcat9425, я не обновляю сама коллекцию tho, я просто обновляю ее элементы, что также можно сделать с помощью свойства getter only –

ответ

2

Проблема, которая у вас есть, заключается в неизменности string. Когда вы меняете текст в TextBox, вы создаете новый string, старый не меняется. Поэтому List всегда содержит одни и те же значения.

Чтобы обновить его, вы должны обернуть его в класс, который реализует INotifyPropertyChanged и запускает событие PropertyChanged в установщике. Также вы должны изменить свою привязку к этому новому свойству. Например:

public class Wrapper : INotifyPropertyChanged 
{ 
    private string _value; 
    public string Value 
    { 
     get { return _value; } 
     set 
     { 
      _value = value; 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value))); 
     } 
    } 

    //INotifyPropertyChanged implementation... 
} 

Ваш список должен быть:

public List<MyClass> Strings { get; } = new List<MyClass> 
{ 
    new Wrapper { Value = "Hello world1" }, 
    new Wrapper { Value = "Hello world2" }, 
    new Wrapper { Value = "Hello world3" } 
}; 

и связывание должно быть:

<TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}" /> 
+0

Это работает, даже без INotifyPropertyChanged. Но я нахожу это странным, так как он все еще делает то же самое, строка по-прежнему неизменна, просто завернутый в другой класс. Мы по-прежнему меняем строку, а теперь и всю ссылку на этот класс. Это связано с тем, как написано Binding? '{Binding Path =.}'? –

+0

Часть 'INotfiyPropertyChanged' необходима, если вы обновляете строку из кода и хотите, чтобы TextBox отображал обновленное значение. Вы можете опустить его, если хотите. Для части привязки да, когда вы изменяете значение в TextBox, всегда создается новая строка. Однако для обновления списка необходимо удалить старое значение и добавить новое значение, и это не так, как работает привязка.Когда у вас есть класс-оболочка, он просто устанавливает вашу строку в новое значение и отбрасывает старое значение (которое вы не можете сделать с «списком»). –

+0

Хорошо. Почему бы ему не работать? Не знает ли система привязки 'Strings [index] = newString;' или тому подобное? –

0

изменение

public List<string> Strings { get; } = new List<string> { "Hello world1", "Hello world2", "Hello world3" }; 

в

public List<string> Strings { get; set;} = new List<string> { "Hello world1", "Hello world2", "Hello world3" }; 

Без сеттера, нет никакого способа, чтобы обновить эту собственность, даже если он привязан.

+0

Строка RW. Список Строки не обновляются - только элементы. Еще стоит попробовать. – Paparazzi

+0

Не работает :( –

0

Я думаю, что ваша проблема - это тип списка, который вы связываете. Вместо этого попробуйте использовать ObservableCollection.

public ObservableCollection<string> Strings { get; } = new ObservableCollection<string> { "Hello world1", "Hello world2", "Hello world3" }; 

Возможно, это сработает.

+0

Я фактически обновляю строки, не меняя коллекцию. Я дал это попробовать, но все равно не работает. –

+0

Извините. Я не в прямом направлении, если бы прямо сейчас, или я бы протестировал это сначала. – Lorek

+1

Вот теория ... Когда ваши свойства текста изменяются, они устанавливают новое значение String, что означает, что они создают новый экземпляр строки. Связывание указывает на эту новую String, которая не является членом исходного списка Эта новая строка ссылается только на привязку к TextBox. Похоже, эта сокращенная привязка к коллекции будет полезна только тогда, когда вы привязываете объект «.» К контейнеру пользовательского интерфейса, а затем привязываете дочерние элементы пользовательского интерфейса к свойствам этого объекта. Для вашего супер-упрощенного случая это терпит неудачу. – Lorek

1

У вас есть две проблемы здесь.

Во-первых, ваш пользовательский интерфейс никогда не узнает, если вы замените список новым списком, поскольку он не получит уведомление, если список <> будет повторно создан.

Во-вторых, любые изменения элементов в Списке, будь то замена значения или добавление/удаление значений, останутся незамеченными, поскольку список не вызывает уведомления об изменениях.

Ваше лучшее решение представляет собой комбинацию ответов, полученных @ qqww2 и @Lorek.

Отключите все данные от класса Window и поместите его в другой класс, который реализует INotifyPropertyChanged. Измените коллекцию строк на ObservableCollection. А также вызывать события PropertyChanged в сеттерах любых свойств, хотя это может не потребоваться для ObservableCollection, пока вы только создаете экземпляр в конструкторе и вызываете только ясность, если вам нужно удалить все записи.

public class DataViewModel : INotifyPropertyChanged 
{ 
    private string _theString; 

    public DataViewModel() 
    { 
     Strings = new ObservableCollection<string>(); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public ObservableCollection<string> Strings { get; set; } 

    public string TheString 
    { 
     get 
     { 
      return _theString; 
     } 
     set 
     { 
      _theString = value; 
      NotifyPropertyChanged(nameof(TheString)); 
     } 
    } 

    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
} 

Внутри вашего класса окна, изменить конструктор:

public MainWindow() 
{ 
    this.InitializeComponent(); 
    this.ViewModel = new DataViewModel(); 
} 

И добавить новое свойство:

public DataViewModel ViewModel { get; set; } 

И изменить DataContext привязки на окна тега, чтобы указать на ViewModel имущество.

Дополнительная информация:

На модели представления, добавьте следующий метод

public void AddString() 
{ 
    Strings.Add(TheString) ; 
    TheString = string.Empty; 
} 

И затем вызвать, что из обработчика нажатия кнопки.

+0

Не работает. Я обновляю ListMembers с использованием пользовательского интерфейса и фактически не обновляется в списке в действительности. Это проблема. 'TheString' обновляется, но члены в ObservableCollection этого не делают. –

+0

Мне, должно быть, что-то не хватает, от вашего вопроса неясно, чего вы пытаетесь достичь – benPearce

-1

Да, я тоже столкнулся с этой проблемой! Это похоже на неизменное поведение типа строки. Я написал также статью с решением по my blog.

Надеется, что это поможет :)