user1325543
user1325543

Reputation: 531

Unable to bind in uwp app vs2017 - also, ListView not scrolling

I'm unable to bind an object to a page in a UWP app using VS2017. I can bind a collection, but not a single object. My code:

User.cs

using System.ComponentModel;

namespace Binding
{
    public class User : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private string _name = "";

        public User(string name)
        {
            Name = name;
        }

        public string Name
        {
            get { return _name; }
            set { _name = value ?? ""; OnPropertyChanged(nameof(Name)); }
        }
    }
}

MainPage.xaml

<Page
    x:Class="Binding.MainPage"
    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"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Button
            x:Name="addBtn" 
            Grid.Row="0"
            Grid.Column="0"
            Click="addBtn_Click"
            Content="Add"/>

        <ListBox 
            x:Name="UserList"
            Grid.Row="1"
            Grid.Column="0"
            ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.VerticalScrollMode="Auto"
            SelectionChanged="UserList_SelectionChanged"
            Height="1000"
            >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <TextBox
            x:Name="NameTextBox"
            Grid.Row="0"
            Grid.Column="1"
            TextChanged="NameTextBox_TextChanged"
            Text="{x:Bind Path=UserViewModel.Name, Mode=TwoWay}"
            />

    </Grid>
</Page>

MainPage.xaml.cs

using System.Collections.ObjectModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Binding
{
    public sealed partial class MainPage : Page
    {
        public ObservableCollection<User> _users { get; } = new ObservableCollection<User>();
        public User UserViewModel { get; set; }

        public MainPage()
        {
            InitializeComponent();
            UserList.ItemsSource = _users;
        }

        private void addBtn_Click(object sender, RoutedEventArgs e)
        {
            _users.Add(new User("Name" + _users.Count));
        }

        private void UserList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (UserList.SelectedItem is User user)
            {
                UserViewModel = user;
                //NameTextBox.Text = $"{user.Name}";
            }
        }

        private void NameTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            //UserViewModel.Name = NameTextBox.Text;
        }
    }
}

If I uncomment the two lines as shown below, I get the effect I'm looking for

private void UserList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (UserList.SelectedItem is User user)
    {
        UserViewModel = user;

        //UNCOMMENTED
        NameTextBox.Text = $"{user.Name}";
    }
}

private void NameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{

    //UNCOMMENTED
    UserViewModel.Name = NameTextBox.Text;
}

However, that isn't what I need. In the actual app I'm developing, there will be a form with several fields that will need two way binding to the UserViewModel instance. I don't want to pepper the form with TextChanged event handlers (or FocusLost, whatever) for every single field on the form.

My second problem is that the ListView doesn't scroll.

Thanks in advance.

Upvotes: 0

Views: 90

Answers (1)

Jayden
Jayden

Reputation: 3286

You should be able to use the Binding to bind TextBox to the UserViewModel. We can set PropertyChanged to UpdateSourceTrigger.

When we set UpdateSourceTrigger to PropertyChanged.The binding source is updated whenever the binding target value changes. This is detected automatically by the binding system.

There is no UpdateSourceTrigger property for x:Bind and it only triggers on lost focus.

For example:

<TextBox
    x:Name="NameTextBox"
    Grid.Row="0"
    Grid.Column="1"
    TextChanged="NameTextBox_TextChanged"
    Text="{Binding Name,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

Code behind:

private void UserList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (UserList.SelectedItem is User user)
    {
        UserViewModel = user; 
        NameTextBox.DataContext = UserViewModel;
    }
}

Form your code, you have set Height="1000" to the ListBox. When the Items' height is larger than 1000, the ListBox can be scrolled. If not, it can not be scrolled. You should be able to remove the Height.

Upvotes: 1

Related Questions