PatH
PatH

Reputation: 105

UWP ListView item binding does not evaluate until pointer leave

I'm not sure if this is a UWP problem, my problem, or XAML in general... I have a custom class called UserGroup. The UserGroup contains an ObservableCollection. The User class just contains additional information about each user. Almost everything displays fine, and there are no binding errors in the output window. What i'm having trouble with is the first ListView Item that is displayed. It the binding appears to not evaluate until I move my mouse over and then away from that control. Once that happens, the binding updates and the string is displayed. Interesting, if I remove the first TextBlock from within the ListView, the problem persists with whatever the "first" item is. If its relevant, i'm using MVVM Light.

Here is the XAML:

<Grid x:Name="GridTrackingContainer"
               RelativePanel.Below="BlkUserGroupName"
               RelativePanel.AlignRightWithPanel="True"
                  RelativePanel.AlignLeftWithPanel="True">
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<GridView Grid.Column="1"  x:Name="GridViewTracking" ItemsSource="{Binding Path=UserGroup.Users}" 
              HorizontalAlignment="Center" HorizontalContentAlignment="Stretch">
    <GridView.ItemContainerStyle>
        <Style TargetType="GridViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        </Style>
    </GridView.ItemContainerStyle>
    <GridView.ItemTemplate>
        <DataTemplate>
            <ListView HorizontalAlignment="Center" Margin="10,0,10,0" >
                <ListView.Resources>
                    <Style TargetType="TextBlock">
                        <Setter Property="HorizontalAlignment" Value="Center"/>
                    </Style>
                </ListView.Resources>
                <TextBlock Text="{Binding Path=NickName}"/>

                <TextBlock Text="{Binding Path=UserStats.TimeStarted, Converter={StaticResource DateConverter},ConverterParameter=\{0:hh:mm tt\}}" 
                                       ToolTipService.ToolTip="Time Started"/>
                <TextBlock Text="{Binding Path=UserStats.TimeUpdated, Converter={StaticResource DateConverter},ConverterParameter=\{0:hh:mm tt\}}" 
                                       ToolTipService.ToolTip="Time Updated"/>
                <TextBlock Text="{Binding Path=UserStats.TimeEnded, Converter={StaticResource DateConverter},ConverterParameter=\{0:hh:mm tt\}}" 
                                       ToolTipService.ToolTip="Time Ended"/>

            </ListView>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

I've inspected the visual tree before and after the binding actually populates the text... Before:

Notice the EvaluatedValue is "0", and IsBindingValid is False

After the pointer enters and exits (I assume this is the correct event): enter image description here

If I change the ListView out for a StackPanel, everything shows correctly immediately. Anyone have any ideas?

UPDATE: here is the UserGroup model:

public class UserGroupModel : INotifyPropertyChanged
{
    public class UserGroup : INotifyPropertyChanged
    {
        public string UserGroupName { get; set; }

        private ObservableCollection<User> _Users;
        public ObservableCollection<User> Users
        {
            get { return _Users; }
            set
            {
                _Users = value;
                NotifyPropertyChanged("Users");
            }
        }

        // Property Change Logic  
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class User : INotifyPropertyChanged
    {
        //public string NickName { get; set; }
        private string _NickName;
        public string NickName
        {
            get { return _NickName; }
            set
            {
                _NickName = value;
                NotifyPropertyChanged("NickName");
            }
        }

        private UserStatistics _UserStats;
        public UserStatistics UserStats
        {
            get { return _UserStats; }
            set
            {
                _UserStats = value;
                NotifyPropertyChanged("UserStats");
            }
        }


        // Property Change Logic  
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class UserStatistics : INotifyPropertyChanged
    {
        private DateTime _TimeStarted;
        public DateTime TimeStarted
        {
            get { return _TimeStarted; }
            set
            {
                _TimeStarted = value;
                NotifyPropertyChanged("TimeStarted");
            }
        }

        private DateTime _TimeUpdated;
        public DateTime TimeUpdated
        {
            get { return _TimeUpdated; }
            set
            {
                _TimeUpdated = value;
                NotifyPropertyChanged("TimeUpdated");
            }
        }

        private DateTime _TimeEnded;
        public DateTime TimeEnded
        {
            get { return _TimeEnded; }
            set
            {
                _TimeEnded = value;
                NotifyPropertyChanged("TimeEnded");
            }
        }

        // Property Change Logic  
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    // Property Change Logic  
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Update #1: If I add a second (identical) Textblock like so..

<TextBlock Text="{Binding NickName}"/>
<TextBlock Text="{Binding NickName}"/>

The second TextBlock populates correctly, and immediately. Maybe this is a bug?

Update #2: A simplified version of my project can be downloaded here: https://github.com/pboyvb/mcve2

I think I have found the problem, just not the solution. My app uses a Frame to navigate to each of the pages. This Frame is located in Shell.xaml. In the code-behind for Shell.xaml I set the initial page to be StatusView. I navigate to different frames using this method:

public void NavigateToPage(MenuItems item)
    {
        switch (item)
        {
            case MenuItems.Status:
                FrameMainView.Navigate(typeof(StatusView));
                break;
            case MenuItems.Tracking:
                FrameMainView.Navigate(typeof(TrackingView));
                break;
        }
    }

If I set the initial page to my TrackingView from within the code-behind on Shell.xaml, then the binding on TrackingView works just fine. So my assumption is, that it has something to do with how I am navigating to different pages (frames). So the question is why, and how do I fix this? I'm using a frame navigation strategy because I wanted all of my pages to be contained within the frame of the Shell.xaml. I am locating my menu button and list, and some other global UI elements in Shell.xaml. And I couldn't figure out a way to leverage the MVVM Navigation service to navigate a specific frame in the Shell.xaml. Whenever I used MVVM Navigation it would navigate using the entire Shell.xaml page/view, and I would lose my menu button entirely.

Upvotes: 0

Views: 620

Answers (1)

chavakane
chavakane

Reputation: 246

Try using x:Bind instead of Binding. https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension

Note: You need to specify your model data type in the DataTemplate tag using x:DataType="modelAlias:yourModel"

<DataTemplate x:Key="SimpleItemTemplate" x:DataType="data:SampleDataGroup">
 <StackPanel Orientation="Vertical" Height="50">
   <TextBlock Text="{x:Bind Title}"/>
   <TextBlock Text="{x:Bind Description}"/>
 </StackPanel>
</DataTemplate>

Upvotes: 0

Related Questions