2
votes

The binding on my ItemsControl ItemsTemplate does not work. I've gone through some other similar stack overflow questions but I cannot figure out the problem with my binding. Can someone please show me what I am doing wrong with my binding?

Excerpt from my MainWindow's ViewModel;

private ObservableCollection<uint> words;
private uint word;
private int numberOfWords;

public ObservableCollection<uint> Words
{
    get
    {
        return this.words;
    }

    set
    {
        this.words = value;

        this.NotifyPropertyChanged(m => m.Words);
    }
}

public uint Word
{
    get
    {
        return this.word;
    }

    set
    {
        this.word = value;

        this.NotifyPropertyChanged(m => m.Word);
    }
}

public int NumberOfWords
{
    get
    {
        return this.numberOfWords;
    }

    set
    {
        this.numberOfWords = value;

        this.NotifyPropertyChanged(m => m.NumberOfWords);

        this.Words.Clear();

        for (uint x = 0; x < value; x++)
        {
            this.Words.Add(this.Word);
        }
    }
}

I have the below ItemsControl inside a user control. The MainWindow has its DataContext set to a ViewModel, which the ItemsControl uses. The ItemsSource binding works and I get however many textboxes I specify, but when putting a value in the TextBox, the binding does not work.

<ItemsControl Grid.Row="0" Grid.Column="1" Grid.RowSpan="8" ItemsSource="{Binding Words}">
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <WrapPanel />
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <TextBox Text="{Binding Word}" Width="125" Height="25" />
    </DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

I saw one post that talks about using this type of binding below, but apparently, I do not understand FindAncestor, so I do not know if I am on the right track or not with this.

Text="{Binding Path=Word, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
1
Word property looks like selected item. Why do you want to bind each TextBox.Text to the same property? Do you mean <TextBox Text="{Binding}" Width="125" Height="25" />? This will bind TextBox to each itemdkozl
I do mean to set binding of each TextBox to each item and did not understand that about binding, but when I change my TextBox binding and run it, I get the following error when setting the NumberOfWords property: Two-way binding requires Path or XPathWannabe Coder
change binding to Text="{Binding Path=.}"dkozl
This doesn't make sense, as the DataContext within the DataTemplate will be one of the uints from the collection Words. uint doesn't have a public property called Word. Also, it wouldn't work even if you were doing it right. You cannot alter a uint inside of a collection via a binding. Bindings don't work like that. You'd need to create a Word class that has a public property of type uint, and you would bind against that property. This is just all kinds of nope here.user1228
@dkozl That looked like it worked on the view because I see a zero in each TextBox now. But I still do not see the property Word getting hit when I enter a value, when I have a breakpoint there. And when I check my property Words, I do not see any change.Wannabe Coder

1 Answers

3
votes

You cannot bind to elements in a collection and then change the element itself--you can only change properties of this element through the binding. In other words, given the following collection

[ "one", "two", "three" ]

through a binding such as

<TextBox Text="{Binding Words[0]} /> <!-- the word "one" is displayed in the tb -->

if you change "one" to "derp" you would not alter the collection to

[ "derp", "two", "three" ]

To apply this to your example, you would want to bind the collection to the ItemsControl, then in the template bind to each instance within the collection and change properties of this instance.

First, create your Model. This holds your data and is what you bind against in the UI.

public sealed class Word 
{ 
    public uint Value {get;set;}
}

Next, expose a collection of these on your View Model.

public sealed class ViewModel
{
    //create and fill in ctor
    public ObservableCollection<Word> WordsYo {get;private set;}
}

Next, bind your ItemsControl's ItemsSource to this property and bind elements in the template to the properties of Word:

<!-- Window.DataContext is set to an instance of ViewModel -->
<ItemsControl ItemsSource="{Binding WordsYo}">
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <TextBox Text="{Binding Value}" />
    </DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

The Aristocrats.