Xaml two way binding of textbox to observable coll

2019-09-09 03:16发布

问题:

In my WP8 app I have a class, which has a ObservableCollection<ObservableCollection<int>> property called Matrix.

I want to display these matrices using items control.

 <ItemsControl ItemsSource="{Binding FirstMatrix.Matrix}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ItemsControl ItemsSource="{Binding}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal"></StackPanel>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBox Text="{Binding}" />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

The code works as far as displaying is concerned (it's filled with zeros which is a default value). But I also want to allow changes in TextBoxes which would be reflected in Matrix property - now the TextBoxes can't be changed, because their value is bound one way to Matrix cells I guess. I tried setting <TextBox Text="{Binding Mode=TwoWay}" />or sth similar but it doesn't seem to work. Any ideas how should the data be bound ?

EDIT: I have implemented the INotifyPropertyChanged. Here is a part of my class:

public partial class CalcMatrix : INotifyPropertyChanged
    {
        public ObservableCollection<ObservableCollection<int>> Matrix 
        { 
            get { return _matrix; }
            set
            {
                _matrix = value;
                OnPropertyChanged("Matrix");
            } 
        }
        private ObservableCollection<ObservableCollection<int>> _matrix;

        private void OnPropertyChanged(string argName)
        {
            var handler = PropertyChanged;
            if(handler != null)
                handler(this, new PropertyChangedEventArgs(argName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

I think the reason the TexBoxes don't change is because the binding is one-way - the Text is always what is inside the Matrix. I believe that i should somehow change the XAML binding to TwoWay or something but don't know how. Any ideas ?

回答1:

Two way mode binding require path (why? see this SO answer), so you can't do it just like {Binding Mode=TwoWay}, it has to be something like {Binding SomePath, Mode=TwoWay}. Therefore, in this case you have to wrap matrix item to be some class instead of plain int and put that int as property's value of that class.

//your Matrix property type become:
...
public ObservableCollection<ObservableCollection<MatrixElement>> Matrix
...

//MatrixElement class is something like:
public class MatrixElement : INotifyPropertyChanged
{
    private int _value;
    public int Value
    {
        get { return _value; }
        set { 
                _value = value; 
                OnPropertyChanged("Value"); 
            }
    }

    ....
}

//then you can bind TextBox in two way mode
...
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <TextBox Text="{Binding Value, Mode=TwoWay}" />
    </DataTemplate>
</ItemsControl.ItemTemplate>
...


回答2:

The reason it didnt work is that the itemsource is a list of Matrix and you are not making any changes to the list iteself like adding or removing from a list instead you are changing a property of the item present in list I assume you are using an ObservableCollection.... So you need to implement a INotifyPropertyChanged interface to tell the UI that Hey I am changed please update yourself....

 class YourClass : INotifyPropertyChanged
{

 private string yourProperty;
 public string YourPropety{
   get{ 
        return yourProperty; 
     }
   set{
         if (value != this.yourProperty)
            {
                this.yourProperty = value;
                NotifyPropertyChanged();
            }
    }
 }       

 public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        } 
    }
 }