it-swarm.dev

GUI'yi (WPF) farklı bir iş parçacığı kullanarak güncelleme

Sadece burada bir problemim var, nasıl düzelteceğimi bilmiyorum. Bir GUI ve seri veri içeren küçük bir proje yapıyorum. GUI ana iş parçacığı tarafından çalıştırılıyor ve gelen seri verilerimi tutan veri değişkenlerinin sürekli olarak güncellenmesi gerektiğinden, bunlar ikinci bir iş parçacığında güncelleniyor. Sorun şu ki, GUI'deki bazı metin kutularını güncellemem gerektiğinde, bunların ikincil diziden gelen verilerle güncellenmesi gerekiyor ve sorunum burada. Bunları doğrudan ikincil iş parçacığından güncelleyemiyorum ve ikincil iş parçacığımdaki verileri nasıl transfer edeceğimi ve bunları ana iş parçacığından güncelleyerek nasıl çalıştıracağımı bilmiyorum. Kodumu aşağıya koydum:

Herhangi bir yardım çok iyi olurdu.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace GUIBike
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static string inputdata;
        public static int MaximumSpeed, maximumRiderInput, RiderInput, Time, CurrentSpeed, DistanceTravelled, MaximumMotorOutput, MotorOutput, InputSpeed;
        public static string SaveDataString;
        public Thread Serial;
        public static SerialPort SerialData;
        public static string[] portlist = SerialPort.GetPortNames();
        public static string[] SaveData = new string[4];
        public static string directory = "C:\\";

        public MainWindow()
        {
            Serial = new Thread(ReadData);
            InitializeComponent();
            int Count = 0;
            for (Count = 0; Count < portlist.Length; Count++)
            {
                ComPortCombo.Items.Add(portlist[Count]);
            }
        }

        private void StartDataButton_Click(object sender, RoutedEventArgs e)
        {
            SerialData = new SerialPort(ComPortCombo.Text, 19200, Parity.None, 8, StopBits.One);
            SerialData.Open();
            SerialData.WriteLine("P");
            Serial.Start();
            StartDataButton.IsEnabled = false;
            EndDataButton.IsEnabled = true;
            ComPortCombo.IsEnabled = false;
            CurrentSpeed = 0;
            MaximumSpeed = 0;
            Time = 0;
            DistanceTravelled = 0;
            MotorOutput = 0;
            RiderInput = 0;
            SaveData[0] = "";
            SaveData[1] = "";
            SaveData[2] = "";
            SaveData[3] = "";
            SaveDataButton.IsEnabled = false;
            if (SerialData.IsOpen)
            {
                ComPortStatusLabel.Content = "OPEN";
                SerialData.NewLine = "/n";
                SerialData.WriteLine("0");
                SerialData.WriteLine("/n");
            }
        }

        private void EndDataButton_Click(object sender, RoutedEventArgs e)
        {
            SerialData.Close();
            SaveDataButton.IsEnabled = true;
            SerialData.WriteLine("1");
            SerialData.WriteLine("0");
            if (!SerialData.IsOpen)
            {
                ComPortStatusLabel.Content = "CLOSED";
            }
            int i = 0;
            for (i = 0; i < 4; i++)
            {
                if (i == 0)
                {
                    SaveDataString = "MaximumSpeed during the Ride was = " + Convert.ToString(MaximumSpeed) + "m/h";
                    SaveData[i] = SaveDataString;
                }
                if (i == 1)
                {
                    SaveDataString = "Total Distance Travelled = " + Convert.ToString(DistanceTravelled) + "m";
                    SaveData[i] = SaveDataString;
                }
                if (i == 2)
                {
                    SaveDataString = "Maximum Rider Input Power = " + Convert.ToString(maximumRiderInput) + "Watts";
                    SaveData[i] = SaveDataString;
                }
                if (i == 3)
                {
                    SaveDataString = "Maximum Motor Output Power = " + Convert.ToString(MaximumMotorOutput) + "Watts";
                    SaveData[i] = SaveDataString;
                }
            }
        }

        private void SaveDataButton_Click(object sender, RoutedEventArgs e)
        {
            //File.WriteAllBytes(directory + "image" + imageNO + ".txt", ); //saves the file to Disk    
            File.WriteAllLines(directory + "BikeData.txt", SaveData);
        }

        public void ReadData()
        {
            int counter = 0;

            while (SerialData.IsOpen)
            {
                if (counter == 0)
                {
                    //try
                    //{
                        InputSpeed = Convert.ToInt16(SerialData.ReadChar());
                        CurrentSpeed = InputSpeed;
                        if (CurrentSpeed > MaximumSpeed)
                        {
                            MaximumSpeed = CurrentSpeed;
                        }
                        SpeedTextBox.Text = "Current Wheel Speed = " + Convert.ToString(CurrentSpeed) + "Km/h";
                        DistanceTravelled = DistanceTravelled + (Convert.ToInt16(CurrentSpeed) * Time);
                        DistanceTravelledTextBox.Text = "Total Distance Travelled = " + Convert.ToString(DistanceTravelled) + "Km";
                    //}
                    //catch (Exception) { }
                }
                if (counter == 1)
                {
                    try
                    {
                        RiderInput = Convert.ToInt16(SerialData.ReadLine());
                        if (RiderInput > maximumRiderInput)
                        {
                            maximumRiderInput = RiderInput;
                        }
                        RiderInputTextBox.Text = "Current Rider Input Power =" + Convert.ToString(RiderInput) + "Watts";
                    }
                    catch (Exception) { }
                }
                if (counter == 2)
                {
                    try
                    {
                        MotorOutput = Convert.ToInt16(SerialData.ReadLine());
                        if (MotorOutput > MaximumMotorOutput)
                        {
                            MaximumMotorOutput = MotorOutput;
                        }

                        MotorOutputTextBox.Text = "Current Motor Output = " + Convert.ToString(MotorOutput) + "Watts";
                    }
                    catch (Exception) { }
                }
                counter++;
                if (counter == 3)
                {
                    counter = 0;
                }
            }
        }

        private void ComPortCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            StartDataButton.IsEnabled = true;
        }


        private void Window_Closed(object sender, RoutedEventArgs e)
        {
            if (SerialData.IsOpen)
            {
                SerialData.Close();
            }
        }
31
user517002

Bu sorunu çözmek için bir temsilci kullanabilirsiniz .. __ İşte burada farklı bir iş parçacığı kullanarak bir textBox'un nasıl güncelleneceğini gösteren bir örnek 

public delegate void UpdateTextCallback(string message);

private void TestThread()
{
    for (int i = 0; i <= 1000000000; i++)
    {
        Thread.Sleep(1000);                
        richTextBox1.Dispatcher.Invoke(
            new UpdateTextCallback(this.UpdateText),
            new object[] { i.ToString() }
        );
    }
}
private void UpdateText(string message)
{
    richTextBox1.AppendText(message + "\n");
}

private void button1_Click(object sender, RoutedEventArgs e)
{
   Thread test = new Thread(new ThreadStart(TestThread));
   test.Start();
}

TestThread yöntemi, textBox'u güncellemek için test adlı iş parçacığı tarafından kullanılır 

28
user913359

orada.

Ayrıca WPF, Kullanarak bir seri port test aracı da geliştiriyorum ve deneyimlerimi paylaşmak istiyorum.

MVVM tasarım modeline göre kaynak kodunuzu yeniden aktive etmeniz gerektiğini düşünüyorum.

Başlangıçta, tanıştığınız aynı sorunu tanıdım ve bu kodu kullanarak çözdüm:

new Thread(() => 
{
    while (...)
    {
        SomeTextBox.Dispatcher.BeginInvoke((Action)(() => SomeTextBox.Text = ...));
    }
}).Start();

Bu işe yarar, ama çok çirkin . Bunu görene kadar nasıl yeniden ateşleneceği hakkında hiçbir fikrim yok: http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick- Eğitici Başlat

Bu, yeni başlayanlar için çok nazik bir adım adım MVVM öğreticisidir .. __ Parlak UI yok, karmaşık mantık yok, sadece MVVM'nin temeli.

23
Aetherus

GUI'nizi ikincil bir diziden güncellemek için Dispatcher.Invoke kullanabilirsiniz.

İşte bir örnek: 

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        new Thread(DoSomething).Start();
    }
    public void DoSomething()
    {
        for (int i = 0; i < 100000000; i++)
        {
               this.Dispatcher.Invoke(()=>{
               textbox.Text=i.ToString();
               });    
        } 
    }
16
Slaven Tojic

GUI'yi Güncellemek İçin Aşağıdaki Yöntemi kullanın.

     Public Void UpdateUI()
     {
         //Here update your label, button or any string related object.

         //Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));    
         Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
     }

O zaman bu yöntemi kullandığınızda Aklınızda bulundurun, aynı nesneyi doğrudan dağıtıcı iş parçacığından doğrudan güncelleştirmeyin, aksi takdirde yalnızca bu güncellenmiş dizeyi alırsınız ve bu yöntem çaresiz/işe yaramaz . yorum yapmadı, ikisinin de erişebilmesinin farklı bir yolu olduğu hemen hemen aynı etkiye sahip.

6
rhatwar007

Akjoshi ve Julio'nun dediği gibi, GUI'yi GUI öğesiyle aynı konu üzerinde fakat arka plan verilerini işleyen yöntemden güncellemek için bir Eylem göndermekle ilgili. Bu kodu, yukarıdaki akjoshi'nin cevabında özel bir formda görebilirsiniz. Bu genel bir versiyondur.

myTextBlock.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                                   new Action(delegate() 
                                      {
                                      myTextBlock.Text = Convert.ToString(myDataObject.getMeData());
                                      }));

Kritik kısım, kullanıcı arayüzünüzün göndericisini çağırmaktır - bu, doğru iş parçacığına sahip olmanızı sağlar.

Kişisel deneyimlerden böyle bir eylemi oluşturmak ve kullanmak çok daha kolay görünüyor. Sınıf düzeyinde bildirmek bana statik/statik olmayan bağlamlarda birçok sorun verdi.

2
Tim

Dispatcher.BeginInvoke kullanmanız gerekir. Test etmedim, ancak this bağlantısını (bu, Julio G tarafından sağlanan bağlantıdır) farklı bir UI kontrollerini nasıl güncelleyeceğinizi daha iyi anlamak için kontrol edebilirsiniz. ReadData() kodunuzu değiştirdim 

public void ReadData()
{
    int counter = 0;

    while (SerialData.IsOpen)
    {
        if (counter == 0)
        {
            //try
            //{
                InputSpeed = Convert.ToInt16(SerialData.ReadChar());
                CurrentSpeed = InputSpeed;
                if (CurrentSpeed > MaximumSpeed)
                {
                    MaximumSpeed = CurrentSpeed;
                }
    SpeedTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(delegate() { SpeedTextBox.Text = "Current Wheel Speed = " + Convert.ToString(CurrentSpeed) + "Km/h"; });//update GUI from this thread


                DistanceTravelled = DistanceTravelled + (Convert.ToInt16(CurrentSpeed) * Time);

    DistanceTravelledTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
        new Action(delegate() {DistanceTravelledTextBox.Text = "Total Distance Travelled = " + Convert.ToString(DistanceTravelled) + "Km"; });//update GUI from this thread

            //}
            //catch (Exception) { }
        }
        if (counter == 1)
        {
            try
            {
                RiderInput = Convert.ToInt16(SerialData.ReadLine());
                if (RiderInput > maximumRiderInput)
                {
                    maximumRiderInput = RiderInput;
                }                       
    RiderInputTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, 
        new Action(delegate() { RiderInputTextBox.Text = "Current Rider Input Power =" + Convert.ToString(RiderInput) + "Watts"; });//update GUI from this thread
            }
            catch (Exception) { }
        }
        if (counter == 2)
        {
            try
            {
                MotorOutput = Convert.ToInt16(SerialData.ReadLine());
                if (MotorOutput > MaximumMotorOutput)
                {
                    MaximumMotorOutput = MotorOutput;
                }
    MotorOutputTextBox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, 
        new Action(delegate() { MotorOutputTextBox.Text = "Current Motor Output = " + Convert.ToString(MotorOutput) + "Watts"; });//update GUI from this thread                        
            }
            catch (Exception) { }
        }
        counter++;
        if (counter == 3)
        {
            counter = 0;
        }
    }
}
1
akshayb

Sanırım burada birkaç seçeneğiniz var.

Bir BackgroundWorker kullanmak olacaktır. Bu, uygulamalarda çoklu kullanım için yaygın bir yardımcıdır. İş parçacığı havuzundan bir arka plan iş parçacığında işlenen bir DoWork olayı ve arka plan iş parçacığı tamamlandığında, ana iş parçacığında geri çağrılan bir RunWorkerCompleted olayı gösterir. Ayrıca, arka plan iş parçacığı üzerinde çalışan kodu denemenin/yakalamanın yararı vardır, böylece işlenmeyen bir istisna, uygulamayı öldürmez.

Bu rotaya gitmek istemiyorsanız, GUI'yi ana iş parçacığına geri güncellemek için bir eylem başlatmak için WPF dağıtıcısı nesnesini kullanabilirsiniz. Rastgele referans:

http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher

Çevresinde başka birçok seçenek var, ancak bunlar akla gelen en yaygın iki seçenek.

0
Jeff

İşte UI metin kutularını güncelleyen tam bir örnek

<Window x:Class="WpfThreading.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:WpfThreading"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="216.84">
<Grid Margin="0,0,2,0">
    <Button Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0"
            VerticalAlignment="Top" Width="75" Click="Button_Click"/>
    <TextBox HorizontalAlignment="Left" Margin="10,35,0,10" TextWrapping="Wrap" Name="mtextBox" Width="87"   VerticalScrollBarVisibility="Auto"/>
    <TextBox HorizontalAlignment="Left" Margin="111,35,0,10" TextWrapping="Wrap" x:Name="mtextBox2" Width="87"   VerticalScrollBarVisibility="Auto"/>
</Grid></Window>

ve kodda 

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        new Thread(DoSomething).Start();
        new Thread(DoSomething2).Start();
    }


    public void DoSomething()
    {
        for (int i = 0; i < 100; i++)
        {
            Dispatcher.BeginInvoke(new Action(() => {
                mtextBox.Text += $"{i.ToString()}{Environment.NewLine}";
            }), DispatcherPriority.SystemIdle);

            Thread.Sleep(100);
        }

    }

    public void DoSomething2()
    {
        for (int i = 100; i > 0; i--)
        {
            Dispatcher.BeginInvoke(new Action(() => {
                mtextBox2.Text += $"{i.ToString()}{Environment.NewLine}";
            }), DispatcherPriority.SystemIdle);

            Thread.Sleep(100);
        }

    }
}
0
nPcomp