Home > Software design >  Binding an integer member of an class object to Textbox in WPF with INotifyPropertyChanged
Binding an integer member of an class object to Textbox in WPF with INotifyPropertyChanged

Time:09-20

I am absolute beginner in WPF and C#. I am trying to populate the textbox with a counter after reading about INotifyPropertyChanged.

Below is my code:

namespace DataBinding
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    /// 

    //https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-property-change-notification?view=netframeworkdesktop-4.8
    public class OPCUAData : INotifyPropertyChanged
    {
        private string m_text;
        private int m_int;

        public event PropertyChangedEventHandler PropertyChanged;

        public OPCUAData()
        { }

        public OPCUAData(string str, int i)
        {
            this.m_text = str;
            this.m_int = i;
        }

        public string OPCUAtext
        {
            get { return m_text; }
            set
            {
                if (value != m_text)
                {
                    m_text = value;
                    OnPropertyChanged();
                }
            }
        }

        public int OPCUAint
        {
            get { return m_int; }
            set
            {
                if (value != m_int)
                {
                    m_int = value;
                    OnPropertyChanged();
                }
            }
        }


    // Create the OnPropertyChanged method to raise the event
    // The calling member's name will be used as the parameter.
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }

public partial class MainWindow : Window
    {
        static OPCUAData opc = new OPCUAData("", 5);
        
        public MainWindow()
        {
            InitializeComponent();

            Thread thread = new Thread(incrementData);
            thread.Start();
        }

        void incrementData()
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();
            while (timer.Elapsed.TotalSeconds < 10)
            {
                opc.OPCUAint = opc.OPCUAint   1;
            }
            timer.Stop();
        }
    }
}

I am expecting my variable opc.OPCUAint to be displayed in the textbox but it is not. Please help about what I am missing here.

Below is the xml.

<Window x:Class="DataBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DataBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBox Text="{Binding ElementName=opc, Path= OPCUAint }" Margin="91,56,454.6,286"></TextBox>
    </Grid>
</Window>

CodePudding user response:

Set the DataContext of your window:

public MainWindow()
{
    InitializeComponent();
    DataContext = opc; // <--

    Thread thread = new Thread(incrementData);
    thread.Start();
}

...and bind directly to a property of it:

<TextBox Text="{Binding OPCUAint}" />

CodePudding user response:

You need to make "opc" public property and modify the binding accordingly.

public OPCUAData opc { get; } = new OPCUAData("", 5);
<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=opc.OPCUAint}"/>

CodePudding user response:

You use a StopWatch to measure time.
You use a timer to execute an operation periodically.

For example use Timer to execute the operation on a background thread or use DispatcherTimer to execute the operation on the UI thread.

public void StartTimer()
{
  // The callback will automatically execute on a background tread.
  // Note that Timer implements IDisposable
  var timer = new Timer(PeriodicOperation);

  // Start the timer.
  timer.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1));
}

private void PeriodicOperation(object state)
{
  opc.OPCUAint  ;
}

If incrementing is the only operation, or you don't execute CPU intensive code in general, don't create a dedicated thread. It will make the performance unnecessarily bad. In this case, and in case of your posted example, use the mentioned DispatcherTimer:

public void StartTimer()
{
  dispatcherTimer = new DispatcherTimer();
  dispatcherTimer.Tick  = PeriodicOperation;
  dispatcherTimer.Interval = TimeSpan.Fromseconds(1);
  dispatcherTimer.Start();
}

private void PeriodicOperation(object sender, EventArgs e)
{
  opc.OPCUAint  ;
}

Then finally to make it work, you must assign the OPCUAData instance to the DataContext of the MainWindow. Avoid defining public static fields or properties:

private OPCUAData OPCUAData { get; }
public MainWindow()
{
  InitializeComponent();

  this.OPCUAData = new OPCUAData("", 5);
  this.DataContext = this.OPCUAData;

  StartTimer();
}

public void StartTimer()
{
  dispatcherTimer = new DispatcherTimer();
  dispatcherTimer.Tick  = PeriodicOperation;
  dispatcherTimer.Interval = TimeSpan.Fromseconds(1);
  dispatcherTimer.Start();
}

private void PeriodicOperation(object sender, EventArgs e)
{
  this.OPCUAData.OPCUAint  ;
}

And bind directly to the DataContext:

<Window>
  <TextBox Text="{Binding OPCUAint}" />
</Window>
  • Related