0
votes

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.

1
Can you share your models - Roy Sanchez
@RoySanchez I've added the UserGroupModel. Thanks - PatH
Where is your Constructor for UserGroup Class? Create a Constructor and Initialize your Class Like Users = new ObservableCollection<User>(); - AVK
I am initializing the UserGroup class in my TrackingViewModel. The UserGroup class contains the ObservableCollection<User> sub class. So in essence, I believe I've already done this. All of the other bindings are working properly within the UserGroup. - PatH
Could you please share a minimal reproducible example that can reproduce your issue? With current code, it's hard to say where the problem is. Besides, I'm not sure why you use a ListView in the DataTemplate. For each item in the GridView, it's just a User, using ListView here makes no sense, StackPanel might be more appropriate. - Jay Zuo

1 Answers

0
votes

Try using x:Bind instead of Binding. https://docs.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>