Member 13571364 Ответов: 2

Обновление текстового поля с несколькими строками с помощью inotifypropertychanged C# WPF


Хороший день,

Я относительно новичок в C# и только что получил навык реализации INotifypropertychanged.

Я работаю над проектом считывания данных с ЭБУ из CANbus, и по мере получения данных я в настоящее время использую INotifypropertychanged для обновления пользовательского интерфейса с полученными данными.

Это работает хорошо и быстро, однако каждый раз, когда данные получены, данные previos перезаписываются(очевидно) - есть ли какой-либо способ сохранить данные в списке или вызвать propertychanged для печати в новой/следующей строке текстового поля каждый раз??

Спасибо

Что я уже пробовал:

Попытался создать наблюдаемую коллекцию, но не смог обновить список.

2 Ответов

Рейтинг:
4

Member 13571364

@Кинжал,

Пожалуйста, смотрите ниже, я попытался сделать простое приложение, освещающее эту проблему,
- Файл MainWindow создается экземпляр MainViewModel, который создает экземпляр класса как TCP подключение.
- Имеющаяся в наличии коллекция создается в классе как TCP подключение
- данные добавляются в наблюдаемую коллекцию "_CanMessage" во время тестирования
- однако он никогда не обновляется до связанного Listview

Я не совсем уверен в именах Привязок в xaml, так что может возникнуть проблема..

Пожалуйста, дайте мне знать, если у вас возникнут дополнительные вопросы.


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





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


Dirk Bahle

Я пытался скомпилировать ваш проект, но, похоже, не могу найти определение для 2 методов:
- Я не могу найти определение для этого слова. Гексенкодирование метод
- Я не могу найти определение для этого слова. Писать метод, указанный в обработчике событий нажатия кнопки _главный.Клиент.Запись данных);

- Можете ли вы обновить, чтобы включить их?

- Я думаю, вы используете MVVMLight, верно?

Member 13571364

Мои извинения, я не знал, что вы собираетесь скомпилировать программу.
- Я обновил классы, и теперь он компилируется
- Я очень новичок в c# MVVM, так что не слишком уверен, что это за легкая версия? придется об этом почитать.

Dirk Bahle

Я могу скомпилировать ваш код сейчас но я получаю 2 сообщения об ошибках в выходных данных TW VS:

Система.Окна.Ошибка данных: 40 : BindingExpression path error: 'Message' свойство не найдено на 'object' "MainWindow' (Name=")'. BindingExpression:Path=Message; DataItem='MainWindow' (Name="); целевой элемент - 'ListView' (Name="); целевое свойство - 'ItemsSource' (тип 'IEnumerable')
Система.Окна.Данные ошибка: 40 : BindingExpression путь ошибка: 'Сообщение' имущество не нашел на 'объект' "MainViewModel' (хэш-код=24112512)'. BindingExpression:путь=сообщение; элемента данных='MainViewModel' (хэш-код=24112512); целевой элемент-это элемент управления ListView' (имя="); целевое свойство ', что ItemsSource' (тип 'интерфейс IEnumerable')

Эти сообщения указывают на то, что ваш код хочет привязаться к свойству Message (см. код XAML), но класс MainViewModel не содержит свойства Message.

Класс MainViewModel содержит только класс сообщений. Но этот класс не может рассматриваться для привязки - здесь релевантны только свойства экземпляра объекта.

Я думаю, вам нужно изменить выражение привязки на:
ItemsSource = "{Привязка Клиента}"

2-я проблема, которую я нашел, находится в корне MainWindow.xaml - вам нужно удалить оператор привязки: DataContext="{Binding RelativeSource={RelativeSource Self}}", так как это говорит MainWindow искать в своем экземпляре on object упомянутые свойства.

Это, вероятно, исправление, чтобы сделать изменения видимыми в пользовательском интерфейсе. Если у вас есть дополнительные проблемы, было бы проще, если бы вы сделали перерыв в этом и завершили учебник по WPF... возможно, фиксация примера кода в Репро GitHub также облегчит поиск и исправление дальнейших проблем ...

Рейтинг:
19

Dirk Bahle

Образец, приведенный ниже, должен работать для элемента управления ListView - вы легко можете заменить элемент управления ListView со списком. Дайте мне знать, если у вас возникнут вопросы по этому поводу.

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


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


Member 13571364

Большое спасибо, Дирк!

Member 13571364

@ Кинжал,

Я реализовал логику из вашего кода в свой проект, и она отлично работает, когда Обертабельная коллекция создается в экземпляре моей MainViewModel, однако если я помещаю код в другой объект, созданный MainViewModel, список заполняется, но не обновляется на стороне пользовательского интерфейса. Похоже, что список заполняется пустыми местами, но когда я отлаживаю его, я вижу правильную информацию в списке.

Есть предложения?

Dirk Bahle

Это звучит так, как будто вам не хватает события NotifyProperty Changed в вашем viewmodel, но его действительно трудно отличить от описательного описания - можно ли поделиться примером проекта кода, который показывает проблему (здесь или на GitHub)? Мне было бы любопытно на это посмотреть и попробовать, чтобы увидеть, если я могу найти, чтобы исправить ...

Member 13571364

Привет Дирк,
Я добавил пример проекта ниже.

Спасибо
Грант