user2511283 user2511283 - 17 days ago 7
C# Question

TextBlock UI not updating with INotifyPropertyChanged

XAML:

<Button Content="Refresh" Margin="0,5,0,0" Width="120" Click="RefreshPlayers"/>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<StackPanel.Resources>
<viewModels:LastRefreshedViewModel x:Key="LastRefreshedViewModel"/>
</StackPanel.Resources>
<TextBlock Text="Last Refreshed:" HorizontalAlignment="Center"/>
<TextBlock DataContext="{StaticResource LastRefreshedViewModel}" Text="{Binding LastRefreshTimeString}" Margin="3,0,0,0"/>
</StackPanel>


MainWindow:

private void RefreshPlayers(object sender, RoutedEventArgs e)
{
RefreshPlayers();
}

private void RefreshPlayers()
{
GetSteamInfo();
_lastRefreshedViewModel.Reset();
}


ViewModel:

public class LastRefreshedViewModel : PropertyChangedBase
{
private int _lastRefreshTime;

public string LastRefreshTimeString
{
get {
return _lastRefreshTime == 0 ? "Never" : _lastRefreshTime + " min";
}
}

public int LastRefreshTime
{
get { return _lastRefreshTime; }
set {
{
if (value == _lastRefreshTime) {
return;
}

_lastRefreshTime = value;
NotifyOfPropertyChange(() => LastRefreshTimeString);
NotifyOfPropertyChange(() => LastRefreshTime);
}}
}

public LastRefreshedViewModel()
{
_lastRefreshTime = 0;
}

public void Update()
{
LastRefreshTime++;
}

public void Reset()
{
LastRefreshTime = 1;
}


whenever i press "Refresh", the LastRefreshTime updates, but the Textblock UI does not. i suspect this is a threading issue however my attempts to use backgroundworkers, threadpools, and the dispatcher have failed.

Answer

You should set datacontext for parent of any control (in your case textblock). eg.

XAML:

<Button Content="Refresh" Margin="0,5,0,0" Width="120" Click="RefreshPlayers"/>
    <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
        <StackPanel.Resources>
            <viewModels:LastRefreshedViewModel x:Key="LastRefreshedViewModel"/>
        </StackPanel.Resources>
        <TextBlock Text="Last Refreshed:" HorizontalAlignment="Center"/>
        <Grid x:Name="myGrid" DataContext="{StaticResource LastRefreshedViewModel}">
            <TextBlock Text="{Binding LastRefreshTimeString}" Margin="3,0,0,0"/>
        </Grid>
    </StackPanel>

Code Behind:

private void RefreshPlayers(object sender, RoutedEventArgs e)
    {            
        if(myGrid.DataContext != null)
        {
            var viewModel = myGrid.DataContext as LastRefreshedViewModel;
            if(viewModel != null)
            {
                viewModel.Reset();
            }
        }
    }

View Model:

public class LastRefreshedViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // This method is called by the Set accessor of each property.
    // The CallerMemberName attribute that is applied to the optional propertyName
    // parameter causes the property name of the caller to be substituted as an argument.
    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private int _lastRefreshTime;

    public string LastRefreshTimeString
    {
        get
        {
            return _lastRefreshTime == 0 ? "Never" : _lastRefreshTime + " min";
        }
    }

    public int LastRefreshTime
    {
        get { return _lastRefreshTime; }
        set
        {
            {
                if (value == _lastRefreshTime)
                {
                    return;
                }

                _lastRefreshTime = value;
                NotifyPropertyChanged("LastRefreshTimeString");
                NotifyPropertyChanged("LastRefreshTime");
            }
        }
    }

    public LastRefreshedViewModel()
    {
        _lastRefreshTime = 0;
    }

    public void Update()
    {
        LastRefreshTime++;
    }

    public void Reset()
    {
        LastRefreshTime = 1;
    }
}