2
votes

I'm new in c# UWP development and I'm trying to change the value of a TextBlock in runtime, but the binding does not work properly.

I'm binding the text property of the TextBlock in XAML to a property on a ViewModel with INotifyPropertyChanged, and the value changes every 10 seconds.

I don't know if it's the correct way to do it, can someone help me?

Thanks in advance!

this is the ViewModel code

class MainPaigeViewModel : INotifyPropertyChanged
{

    public MainPaigeViewModel()
    {
        Task.Run(async () =>
        {
            Random random = new Random();
            while (true)
            {
                await Task.Delay(10000);
                int newValue = random.Next(-40, 40);
                _MyValue = newValue.ToString();
                Debug.WriteLine(MyValue);
            }
        });
    }

    //Properties
    private string _MyValue;
    public string MyValue
    {
        get { return _MyValue; }
        set
        {
            _MyValue = value;
            RaisePropertyChanged("MyValue");
        }
    }


    //INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

 and the XAML code

   <Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CountDown2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ViewModels="using:CountDown2.ViewModels"
    x:Class="CountDown2.MainPage"
    mc:Ignorable="d">

    <Page.DataContext>
        <ViewModels:MainPaigeViewModel/>
    </Page.DataContext>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <RelativePanel VerticalAlignment="Center" HorizontalAlignment="Center">
            <TextBlock Text="{Binding MyValue, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" 
                       Width="100"
                       Height="40"
                       TextAlignment="Center"
                       FontSize="20"
            />
        </RelativePanel>
    </Grid>
</Page>
2
can you put a breakpoint in Raisepropertychange method and check if it is firing? - riteshmeher
Please provide your code in-place in your question, not as external links. Look here why it's important to give a minimal reproducible example. - dymanoid
@riteshmeher RaisePropertyChanged is not firing, what could be the problem? - CGuiVi

2 Answers

1
votes

Try:Text="{Binding MyValue, Mode=TwoWay}"

1
votes

In UWP unlike silver light and WPF the default binding is One time for performance reasons. The Binding only takes place once as the application starts up. One way binding is the default of WinRT, Silverlight and wpf. Meaning the view will be updated but updating the view will not update view model. Two way binding will update both the view and the view model.

So for a <TextBlock> in the example, it is recommended to use One Way binding.

In a <TextBox> it is recommended to use Two Way binding for user input.

I found a couple small bugs that were causing the binding to fail ... so I changed the viewmodel... The private property was being used rather than public one. Since the code is updating the value in a thread, and then trying to marshal the objects across threads, a dispatcher was added. Also added a common base class for all view models. This make property binding a little easier, it stops binding issues when refactoring property names.

Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync

public class MainPaigeViewModel: ViewModelBase

{


    public MainPaigeViewModel()
    {
        Task.Run(async () =>
        {
            Random random = new Random();
            while (true)
            {
                await Task.Delay(1000);
                int newValue = random.Next(-40, 40);
                try
                {
                    await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                    () => {
                        MyValue = newValue.ToString();
                    });

                }
                catch (Exception ex)
                {
                    string s = ex.ToString();
                }
                Debug.WriteLine(MyValue);
            }
        });
    }

    //Properties
    private string _MyValue;
    public string MyValue
    {
        get { return _MyValue; }
        set
        {
            _MyValue = value;
            OnPropertyChanged();
        }
    }


}



public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };


    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {

        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

I also changed the view to use x:binding. I like x:binding over the old data binding because it shows binding issues at compile time rather than at runtime. This is besides the performance enhancements it gives.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <RelativePanel VerticalAlignment="Center" HorizontalAlignment="Center">
            <TextBlock Text="{x:Bind viewModel.MyValue, Mode=OneWay}" 
                       Width="100"
                       Height="40"
                       TextAlignment="Center"
                       FontSize="20"
            />
        </RelativePanel>
    </Grid>

Page behind code for x:bind

public sealed partial class MainPage : Page
{
    public MainPaigeViewModel viewModel;

    public MainPage()
    {
        this.InitializeComponent();

        viewModel = new MainPaigeViewModel();
    }


}