Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic textbox binding to a list

I am very new to MVVM and I am stuck with data binding. I have a button on my view page which dynamically creates text boxes but I cannot see how I bind these textboxes to my List in ViewModel.

In my view i have:

<Button x:Name="btWebsite" Grid.ColumnSpan="2" Width="50" Height="50" Click="btWebsite_Click" Margin="23,245,259,202">
        <StackPanel x:Name="pnWebsiteButton" Orientation="Horizontal">
            <Image x:Name="imgWebsite" Source= "Images/webIcon.jpg" Stretch="Fill"  HorizontalAlignment="Left" VerticalAlignment="Top"/>
        </StackPanel>
    </Button>
    <GroupBox x:Name="grpWebsite" VerticalAlignment="Top" HorizontalAlignment="Left"  Margin="73,245,0,0" Grid.ColumnSpan="2" Height="51" Width="170" BorderBrush="{x:Null}" BorderThickness="0">
        <ScrollViewer x:Name="pnScrollWebsite" VerticalScrollBarVisibility="Auto"  HorizontalScrollBarVisibility="Disabled" Margin="0,0,0,-6">
            <StackPanel x:Name="pnWebsite" Orientation="Vertical" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="1,2,0,0" VerticalAlignment="Top" IsEnabled="True">

            </StackPanel>
        </ScrollViewer>
    </GroupBox>

The code behind the button is:

private void btWebsite_Click(object sender, RoutedEventArgs e)
{
    var newTextBox = new TextBox();
    newTextBox.Text = "type the website address...";
    newTextBox.Foreground = Brushes.Gray;
    newTextBox.Width = 150;
    newTextBox.Name = "txtWebsite" + iWebsites;
    pnWebsite.Children.Add(newTextBox);
    pnWebsite.RegisterName(newTextBox.Name, newTextBox);

    iWebsites++;
}

In my ViewModel i have:

public List<string> Websites
{
    get { return _websites; }
    set
    {
        if (value != _websites)
        {
            _websites = value;
            OnPropertyChanged("Websites");
        }
    }
}

I am struggling to see how I get the website textboxes into the viewmodel list. Thank you for your help

like image 259
Theresa Ferguson Avatar asked Feb 07 '17 14:02

Theresa Ferguson


1 Answers

Delete your event handler from the code-behind: btWebsite_Click.

Modify your xaml like this:

<Button x:Name="btWebsite" Grid.ColumnSpan="2" Width="50" Height="50" Command="{Binding AddNewStringCommand}" Margin="23,245,259,202">
        <StackPanel x:Name="pnWebsiteButton" Orientation="Horizontal">
            <Image x:Name="imgWebsite" Source= "Images/webIcon.jpg" Stretch="Fill"  HorizontalAlignment="Left" VerticalAlignment="Top"/>
        </StackPanel>
</Button>
<GroupBox x:Name="grpWebsite" VerticalAlignment="Top" HorizontalAlignment="Left"  Margin="73,245,0,0" Grid.ColumnSpan="2" Height="51" Width="170" BorderBrush="{x:Null}" BorderThickness="0">
    <ScrollViewer x:Name="pnScrollWebsite" VerticalScrollBarVisibility="Auto"  HorizontalScrollBarVisibility="Disabled" Margin="0,0,0,-6">
        <StackPanel x:Name="pnWebsite" Orientation="Vertical" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="1,2,0,0" VerticalAlignment="Top" IsEnabled="True">

            <ItemsControl ItemsSource="{Binding Websites}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBox Text="{Binding Mode=TwoWay}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

        </StackPanel>
    </ScrollViewer>
</GroupBox>

You need to modify your ViewModel also:

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

public ICommand AddNewStringCommand => new RelayCommand(AddNewString);

private void AddNewString()
{
    Websites.Add(string.Empty);
}

Instead of RelayCommand you can use any implementation of ICommand. I use for example MVVMLight.

As you see, the main differences:

  • Instead of handling the Click event, there is a Command Binding in the button.
  • Instead of generating new controls from code behind, there is an ItemsControl that creates one every time when we have a new element in the collection.
  • The new TextBox's Text property is bound to the new element.

Update:

I made a mistake, ObservableCollection is not working directly with the TextBox.

It seems you need something in addition:

public class Website
{
    public string Name { get; set; }
}

Modify the ViewModel like this:

public ObservableCollection<Website> Websites { get; set; } = new ObservableCollection<Website>();

public ICommand AddNewStringCommand => new RelayCommand(AddNewString);

private void AddNewString()
{
    Websites.Add(new Website {Name = "new website"});
}

And the ItemsTemplate like this:

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <StackPanel >
            <TextBox Text="{Binding Name, Mode=TwoWay}"/>
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>
like image 68
jannagy02 Avatar answered Nov 09 '22 00:11

jannagy02