user2511283 user2511283 - 10 months ago 47
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 Source

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;
    }
}