Click here to Skip to main content
15,886,026 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Good day,

I am relatively new to C# and have just got the hang of implementing INotifypropertychanged.

I am working on a project reading data off an ECU from the CANbus, and as I receive the data I am currently using INotifypropertychanged to update the UI with the received data.

This works well and fast however each time data is received, the previos data is overwritten(obviously) - is there any way to store the data in a list or call propertychanged to print in a new/next line of the textbox each time??

Thanks

What I have tried:

Tried creating an observable collection but was not able to update the list.
Posted
Updated 30-May-18 1:31am

The sample below should work for a ListView - you can easily replace the listview with a listbox. Let me know if you have questions about it.

C#
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace ListViewTest.Test2
{
  public partial class ListViewTest : Window
  {
    /// <summary>
    /// ObservableCollection to store data displayed in listview
    /// </summary>
    ObservableCollection<GameData> _GameCollection = new ObservableCollection<GameData>();

    /// <summary>
    /// Standard Constructor fills up the ListView
    /// via Observable collection with three items
    /// </summary>
    public ListViewTest()
    {
      _GameCollection.Add(new GameData
      {
        GameName = "World Of Warcraft",
        Creator = "Blizzard",
        Publisher = "Blizzard"
      });

      _GameCollection.Add(new GameData
      {
        GameName = "Halo",
        Creator = "Bungie",
        Publisher = "Microsoft"
      });

      _GameCollection.Add(new GameData
      {
        GameName = "Gears Of War",
        Creator = "Epic",
        Publisher = "Microsoft"
      });

      InitializeComponent();
    }

    public ObservableCollection<GameData> GameCollection
    {
      get { return _GameCollection; }
    }

    /// <summary>
    /// Click button event adds a new element to the ObservableCollection
    /// each time the button is clicked -> the event is forwarded to listview
    /// which displays the change later on...
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void AddRow_Click(object sender, RoutedEventArgs e)
    {
      _GameCollection.Add(new GameData
      {
        GameName = "A New Game",
        Creator = "A New Creator",
        Publisher = "A New Publisher"
      });
    }
  }

  /// <summary>
  /// Simple class to be stored in ObservableCollection and to be displayed in Listview
  /// </summary>
  public class GameData
  {
    public string GameName { get; set; }
    public string Creator { get; set; }
    public string Publisher { get; set; }
  }
}


XML
<Window x:Class="ListViewTest.Test2.ListViewTest"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   DataContext="{Binding RelativeSource={RelativeSource Self}}"
   Title="Some Game Data" Height="216" Width="435">

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <TextBlock Grid.Row="0" TextWrapping="WrapWithOverflow" Margin="3">
    This smaple demonstates how to implment a ListView with static information in XAML (see ListView project)
    and how to populate information in a ListView via bound data source using an
    <TextBlock FontWeight="Bold">ObservableCollection</TextBlock>
     (see ListViewTest project).
    </TextBlock>

    <StackPanel Grid.Row="1">
      <ListView ItemsSource="{Binding GameCollection}">
        <ListView.View>
          <GridView>
            <GridViewColumn Width="140" Header="Game Name" DisplayMemberBinding="{Binding GameName}"  />
            <GridViewColumn Width="140" Header="Creator"   DisplayMemberBinding="{Binding Creator}" />
            <GridViewColumn Width="140" Header="Publisher" DisplayMemberBinding="{Binding Publisher}" />
          </GridView>
        </ListView.View>
      </ListView>
      <Button HorizontalAlignment="Right" Margin="5,5,5,5"
      Content="Add Row" Click="AddRow_Click" />
    </StackPanel>
  </Grid>
</Window>
 
Share this answer
 
Comments
Member 13571364 28-May-18 5:34am    
Thanks very much Dirk!
Member 13571364 29-May-18 2:09am    
@ Dirk,

I've implemented the logic from your code into my project and it works fine when the Obervable collection is created within the instance of my MainViewModel, however if I place the code in another object instantiated by the MainViewModel, the list is being populated however not updated on the UI side. It looks like the list is being filled with blank spaces but when I debug through I can see the correct information in the list.

Any suggestions?
Dirk Bahle 29-May-18 2:23am    
This sound like you are missing a NotifyProperty Changed event in your viewmodel but its really difficult to tell from a narrative description - is it possible to share a sample code project that shows the problem (here or on GitHub)? I'd be curious to look at it and try to see if I can find a fix ...
Member 13571364 30-May-18 7:33am    
Hi Dirk,
I've added the sample project below.

Thanks
Grant
@Dirk,

Please see below, I have tried to make a simple application highlighting the issue,
- Mainwindow creates an instance of MainViewModel which creates an instance of the TcpConnection class.
- The observable collection is created in the TcpConnection class
- data is added to the observable collection "_CanMessage" during testing
- however its is never updated to the bound Listview

I am not entirely sure about the binding names in the xaml so there could be the issue..

Please let me know if you have any further questions.


C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Sockets;
using System.Threading;
using System.Windows;
using static SampleProj.MainViewModel;

namespace SampleProj
{
    public partial class MainWindow : Window
    {
        MainViewModel _main;
        public MainWindow()
        {
            InitializeComponent();
            _main = new MainViewModel();
            DataContext = _main;
        }

        private void Connect_Button_Click(object sender, RoutedEventArgs e)
        {
            _main.Client.Connect();
        }

        private void Start_Button_Click(object sender, RoutedEventArgs e)
        {
            byte[] data = { 42, 0, 17, 2, 0, 22, 0, 0, 22, 43 };
            _main.Client.Write(data);
        }
    }

    class MainViewModel : ObservableObject
    {
        public TcpConnection Client { get; private set; }

        public MainViewModel()
        {
            Client = new TcpConnection();
        }

        public class Message
        {
            public string CANMessage { get; set; }
        }
    }

    public class TcpConnection : ObservableObject
    {
        TcpClient client;
        Thread threadClientReceive;
        NetworkStream clientStream;

        ObservableCollection<Message> _CanMessage = new ObservableCollection<Message>();

        bool receiveThreadAlive = false;
        bool flagThreadRunning = false;
        bool clientIsConnected = false;

        string identifier = string.Empty;
        string data = string.Empty;

        public TcpConnection()
        {

        }

        public void Connect()
        {
            client = new TcpClient();
            var result = client.BeginConnect("192.168.0.1", 10000, null, null);
            var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2));
            threadClientReceive = new Thread(new ParameterizedThreadStart(TcpReceive));
            flagThreadRunning = true;
            receiveThreadAlive = true;
            threadClientReceive.Start(client);

            if (!success)
            {
                throw new Exception();
            }

            if (client.Connected)
            {
                clientIsConnected = true;
            }
        }


        public void TcpReceive(object client)
        {
            string bitString = string.Empty;
            bool startBitFound = false;
            byte startBit = 42;
            byte stopBit = 43;
            List<byte> receiveDataList = new List<byte>();

            TcpClient tcpClient = (TcpClient)client;
            if (tcpClient.Connected)
            {
                clientStream = tcpClient.GetStream();
            }

            byte[] buffer = new byte[1024];
            while (flagThreadRunning)
            {
                int bytesRead = 0;
                try
                {
                    bytesRead = clientStream.Read(buffer, 0, buffer.Length);
                }
                catch { }

                if (bytesRead == 0)
                {
                    break;
                }

                for (int i = 0; i < buffer.Length; i++)
                {
                    if (buffer[i] == startBit)
                    {
                        startBitFound = true;
                    }

                    if (startBitFound)
                    {
                        receiveDataList.Add(buffer[i]);
                    }

                    if (buffer[i] == stopBit)
                    {
                        if (receiveDataList.Count == 0)
                            break;
                        else
                        {
                            string message = HexEncoding.ToString(receiveDataList.ToArray());
                            if (message.Length > 0)
                            {
                                _CanMessage.Add(new Message { CANMessage = message });
                            }
                            startBitFound = false;
                            receiveDataList.Clear();
                            break;
                        }

                    }
                }
            }
        }


        public void Write(byte[] data)
        {
            clientStream = client.GetStream();
            try
            {
                clientStream.Write(data, 0, data.Length);
            }
            catch
            {
            }
        }
    }

    public class HexEncoding
    {
        public HexEncoding()
        {

        }

        public static string ToString(byte[] bytes)
        {
            string hexString = "";

            for (int i = 0; i < bytes.Length; i++)
            {
                hexString += bytes[i].ToString("X2");
            }
            return hexString;
        }
    }


    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}





C#
        <Window x:Class="SampleProj.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:SampleProj"
        
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="525">
    <Grid Margin = "0,0,-6.6,-242.2" >
        < Button Name="conect" Content="Connect" HorizontalAlignment="Left" Margin="119,37,0,0" VerticalAlignment="Top" Width="75" Click="Connect_Button_Click"/>
        <Button Name = "start" Content="Start Session" HorizontalAlignment="Left" Margin="240,37,0,0" VerticalAlignment="Top" Width="100" Click="Start_Button_Click"/>

        <ListView ItemsSource = "{Binding Message}" Grid.ColumnSpan="5" Grid.Column="2" HorizontalAlignment="Left" Height="489" Margin="29.4,65.6,0,0" Grid.Row="2" Grid.RowSpan="11" VerticalAlignment="Top" Width="429">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width = " 200" Header="Message" DisplayMemberBinding="{Binding CANMessage}" ></GridViewColumn>

                </GridView>
            </ListView.View>
        </ListView>

    </Grid>
</Window>
 
Share this answer
 
v2
Comments
Dirk Bahle 30-May-18 8:00am    
I tried to compile your project but I cannot seem to find the definition for 2 methods:
- I cannot find a definition for the HexEncoding method
- I cannot find a definition for the Write method stated in the button click event handler _main.Client.Write(data);

- Can you update to include these?

- I guess you are using MVVMLight, right?
Member 13571364 30-May-18 9:28am    
My appologies I didnt realise you were going to compile the program.
- I have updated the classes, and it compiles now
- I am very new to c# MVVM so not too sure what the light version is? will have to have a read about it.
Dirk Bahle 1-Jun-18 5:55am    
I can compile your code now but I get 2 error messages in the Output TW of VS:

System.Windows.Data Error: 40 : BindingExpression path error: 'Message' property not found on 'object' ''MainWindow' (Name='')'. BindingExpression:Path=Message; DataItem='MainWindow' (Name=''); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
System.Windows.Data Error: 40 : BindingExpression path error: 'Message' property not found on 'object' ''MainViewModel' (HashCode=24112512)'. BindingExpression:Path=Message; DataItem='MainViewModel' (HashCode=24112512); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')

These messages indicate that your code wants to bind to a property called Message (see XAML code) but the MainViewModel class does not contain a Message property.

The MainViewModel class only contains a Message class. But that class cannot be considered for binding - only instantiated object properties are relevant here.

I think you need to change the binding expression to:
ItemsSource = "{Binding Client}"

The 2nd Problem I found is in the root of the MainWindow.xaml - you need to remove the binding statement: DataContext="{Binding RelativeSource={RelativeSource Self}}" since this tells the MainWindow to look on its on object instance for the mentioned properties.

This is probably the fix to make changes visible in the UI. If have further problems it would be easier if you take a break from this and complete a tutorial on WPF... maybe commiting the sample code to a GitHub repro could also make it easier to find and fix further problems ...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900