Jump to content

Questions about Threads

RockiLocky
Go to solution Solved by Nuluvius,

So if I am correct and understood you right I am doing it right, but why is it not working then might you be able to help me on that? And thanks for the links will check them out.

 

It's not working because in your code you are calling Dispatcher from the context of a different thread, see this explanation for what's going on. As a side point you should use a Task instead of a Thread as the former offers a higher level of abstraction, proper cancellation, a managed thread pool and more.

 

So if I am correct and understood you right I am doing it right, but why is it not working then might you be able to help me on that? And thanks for the links will check them out.

 

Would you mind giving me a brief story of MVVM like what it is? I know it is Model View ViewModel. But that doesn't help me alot. I did look into that like 2months ago but I didn't get anything that was my problem. D:

 

Certainly. I know it can look rather complicated when you meet it for the first time but trust me it's rather simple once you have persevered with it. It probably doesn't help that, because by its very nature i.e. being a design pattern, it is open to interpretation thus there's absolutely tons of different takes on its implementation floating around the internet.

 

Let's take a visual/tactile approach first. Have some code to play with (this is a very basic implementation of the pattern):

 

Create a new WPF application and paste all of this in verbatim:

MainWindow.xaml

MainWindow.xaml.cs

MainWindowViewModel.cs

 

When we implement MVVM we are interested specifically in separation of concerns and encapsulation. We want our View to be just that, a UI and nothing more. It shouldn't have to know anything about the VIewModel (the business logic) and vice versa the VIewModel shouldn't need to know anything about the View. Then you have the Model which is usually where you have a database interaction layer or compute model or so on and again this should be kept separated by a clean and clearly defined interface - nothing should ever cross over between any of the layers.

 

Communication between layers is achieved by various abstraction patterns/mechanisms such as ViewModelLocators, Data Bindings, Dependency Injection, Inversion of Control, events, interaction requests, Dependency Properties, messages and others.

 

The pattern naturally promotes a clean and very testable design where one should also be able to swap any of the layers out at a later stage without needing to change any dependant code.

<Window x:Class="WpfApplication1.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:viewModel="clr-namespace:WpfApplication1"        mc:Ignorable="d"                Title="MainWindow" Height="150" Width="800">    <Window.DataContext>        <viewModel:MainWindowViewModel/>    </Window.DataContext>    <Grid>        <ProgressBar Minimum="{Binding Minimum}" Maximum="{Binding Maximum}" Value="{Binding Current}"/>        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Text}"/>    </Grid></Window> 
namespace WpfApplication1{    public partial class MainWindow    {        public MainWindow()        {            InitializeComponent();        }    }}
using System.ComponentModel;using System.Threading;using System.Threading.Tasks;namespace WpfApplication1{    internal class MainWindowViewModel : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        private string _text;        private int _current;        public MainWindowViewModel()        {            Text = string.Empty;            Task.Run(                () =>                    {                        var inc = 0;                        while (true)                        {                            if (Current <= 0)                            {                                inc = 1;                            }                            if (Current >= 100)                            {                                inc = -1;                            }                            Current += inc;                            Text = $"{Current}/{Maximum}";                            Thread.Sleep(50);                        }                    });        }        public string Text        {            get            {                return _text;            }            private set            {                _text = value;                OnPropertyChanged("Text");            }        }        public int Minimum { get; private set; }        public int Maximum { get; } = 100;        public int Current        {            get            {                return _current;            }            set            {                _current = value;                OnPropertyChanged("Current");            }        }        private void OnPropertyChanged(string propertyName = null)        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));        }    }}

Hey guys,

 

I will start off with my problem. I want to change an UI through a Thread. So before you start screaming google there are plenty of solutions and so on, I wanna say I did. I saw alot of solutions, but I am not on a high enough level to know everything that is being listed in there and do not know how to change it for my use.

 

[spoiler = Code]

 

using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Threading;
 
namespace TicTacToe
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        char turn = 'p';
        public char[] Fieldcontent = new char[] {'0', '0', '0', '0', '0', '0', '0', '0', '0', };
        public bool test;
        ImageSource Image;
        Thread WinCheck;
 
        public MainWindow()
        {
            InitializeComponent();
            WinCheck = new Thread(new ThreadStart(WC_Main));
            WinCheck.IsBackground = true;
            WinCheck.Start();
        }
 
 
        private void Window_Activated(object sender, EventArgs e)
        {
            DefaultSettings();
        }
 
        private void BTL_Click(object sender, RoutedEventArgs e)
        {
            if(Fieldcontent[0] == '0')
            {
                Turn();
                ITL.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[0] = 'x';
                }
                if(turn == 'a')
                {
                    Fieldcontent[0] = 'o';
                }
            }
        }
        private void BML_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[1] == '0')
            {
                Turn();
                IML.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[1] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[1] = 'o';
                }
            }
        }
        private void BBL_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[2] == '0')
            {
                Turn();
                IBL.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[2] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[2] = 'o';
                }
            }
        }
        private void BTM_Click(object sender, RoutedEventArgs e)
        {
            Turn();
            ITM.Source = Image;
            if (Fieldcontent[3] == '0')
            {
                if (turn == 'p')
                {
                    Fieldcontent[3] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[3] = 'o';
                }
            }
        }
        private void BMM_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[4] == '0')
            {
                Turn();
                IMM.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[4] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[4] = 'o';
                }
            }
        }
        private void BBM_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[5] == '0')
            {
                Turn();
                IBM.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[5] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[5] = 'o';
                }
            }
        }
        private void BTR_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[6] == '0')
            {
                Turn();
                ITR.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[6] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[6] = 'o';
                }
            }
        }
        private void BMR_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[7] == '0')
            {
                Turn();
                IMR.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[7] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[7] = 'o';
                }
            }
        }
        private void BBR_Click(object sender, RoutedEventArgs e)
        {
            if (Fieldcontent[8] == '0')
            {
                Turn();
                IBR.Source = Image;
                if (turn == 'p')
                {
                    Fieldcontent[8] = 'x';
                }
                if (turn == 'a')
                {
                    Fieldcontent[8] = 'o';
                }
            }
        }
        public void Turn()
        {
            if(turn == 'p')
            {
                FigureCross();
                turn = 'a';
            }
            else if(turn == 'a')
            {
                FigureCircle();
                turn = 'p';
            }
        }
        public void DefaultSettings()
        {
            int WIDTH = (int)BrTL.ActualWidth;
            int HEIGHT = (int)BrTL.ActualHeight;
            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                drawingContext.DrawRectangle(Brushes.Silver, null, new Rect(2.5, 2.5, WIDTH - 2.5, HEIGHT - 2.5));
            }
            RenderTargetBitmap bitmap = new RenderTargetBitmap(WIDTH, HEIGHT, 96, 96, PixelFormats.Default);
            bitmap.Render(drawingVisual);
            ITL.Source = bitmap;
            IML.Source = bitmap;
            IBL.Source = bitmap;
            ITM.Source = bitmap;
            IMM.Source = bitmap;
            IBM.Source = bitmap;
            ITR.Source = bitmap;
            IMR.Source = bitmap;
            IBR.Source = bitmap;
        }
        public void FigureCross()
        {
            int WIDTH = (int)BrTL.ActualWidth;
            int HEIGHT = (int)BrTL.ActualHeight;
            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                drawingContext.DrawRectangle(Brushes.Silver, null, new Rect(2.5, 2.5, WIDTH - 2.5, HEIGHT - 2.5));
                drawingContext.DrawLine(new Pen(Brushes.Red, 1), new Point(5, 5), new Point(WIDTH - 5, HEIGHT - 5));
                drawingContext.DrawLine(new Pen(Brushes.Red, 1), new Point(WIDTH - 5, 5), new Point(5, HEIGHT - 5));
            }
            RenderTargetBitmap bitmap = new RenderTargetBitmap(WIDTH, HEIGHT, 96, 96, PixelFormats.Default);
            bitmap.Render(drawingVisual);
            Image = bitmap;
        }
        public void FigureCircle()
        {
            int WIDTH = (int)BrTL.ActualWidth;
            int HEIGHT = (int)BrTL.ActualHeight;
            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                drawingContext.DrawRectangle(Brushes.Silver, null, new Rect(2.5, 2.5, WIDTH - 2.5, HEIGHT - 2.5));
                drawingContext.DrawEllipse(null, new Pen(Brushes.Blue, 1), new Point(WIDTH / 2, HEIGHT / 2), (WIDTH / 2) - 5, (HEIGHT / 2) - 5);
            }
            RenderTargetBitmap bitmap = new RenderTargetBitmap(WIDTH, HEIGHT, 96, 96, PixelFormats.Default);
            bitmap.Render(drawingVisual);
            Image = bitmap;
        }
        public void FigureWin()
        {
            int WIDTH = (int)BrTL.ActualWidth;
            int HEIGHT = (int)BrTL.ActualHeight;
            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                drawingContext.DrawRectangle(Brushes.Silver, null, new Rect(2.5, 2.5, WIDTH - 2.5, HEIGHT - 2.5));
                drawingContext.DrawLine(new Pen(Brushes.Red, 1), new Point(5, 5), new Point(WIDTH - 5, HEIGHT - 5));
                drawingContext.DrawLine(new Pen(Brushes.Red, 1), new Point(WIDTH - 5, 5), new Point(5, HEIGHT - 5));
            }
            RenderTargetBitmap bitmap = new RenderTargetBitmap(WIDTH, HEIGHT, 96, 96, PixelFormats.Default);
            bitmap.Render(drawingVisual);
            Image = bitmap;
        }
        public void WC_Main()
        {
            while (true)
            {
                if (Fieldcontent[0] == 'x' && Fieldcontent[3] == 'x' && Fieldcontent[6] == 'x')
                {
                    Dispatcher.BeginInvoke((Action)delegate
                    {
                        Win();
                    });
                }
                else if (Fieldcontent[0] == 'o' && Fieldcontent[3] == 'o' && Fieldcontent[6] == 'o')
                {
                    Win();
                }
            }
        }
        public void Win()
        {
            BTL.IsEnabled = false;
            BML.IsEnabled = false;
            BBL.IsEnabled = false;
            BTM.IsEnabled = false;
            BMM.IsEnabled = false;
            BBM.IsEnabled = false;
            BTR.IsEnabled = false;
            BMR.IsEnabled = false;
            BBR.IsEnabled = false;
        }
    }
 
}

 

It may look totally shit and it might be but I amt rying to learn, meaning if you have anything to add with my structure meaning I could write that better or make that in another way I would really appriciate that, because I learn through it.

 

Before I go back to my question I would like to explain what a Dispatcher is in my point of view, so I would like you to tell me then if I am right or if I am misunderstanding it completely. A Dispatcher is connection between the thread it is asigned to and other threads/the main programm/classes. Meaning I can use the dispatcher to change variables and such and do events if I am right.

 

 

So now back to the question. What am I doing wrong and what should I do?

 

 

Hope you guys can help me out.

 

Nice Greets,

RockiLocky

Link to comment
Share on other sites

Link to post
Share on other sites

I will start off with my problem. I want to change an UI through a Thread. So before you start screaming google there are plenty of solutions and so on, I wanna say I did. I saw alot of solutions, but I am not on a high enough level to know everything that is being listed in there and do not know how to change it for my use.

 

Before I go back to my question I would like to explain what a Dispatcher is in my point of view, so I would like you to tell me then if I am right or if I am misunderstanding it completely. A Dispatcher is connection between the thread it is asigned to and other threads/the main programm/classes. Meaning I can use the dispatcher to change variables and such and do events if I am right.

 

So now back to the question. What am I doing wrong and what should I do?

 

Yes you are right, you need to acquire the Dispatcher instance from the UI context and then from your thread (for which I recommend you use a Task) call either Invoke or BeginInvoke on it (depending on your needs) with your lambda. There's a more detailed explanation of the Dispatcher here and here. The latter touches on the SynchronizationContext of which there's only a implementation for the UI thread provided by default.

 

It may look totally shit and it might be but I amt rying to learn, meaning if you have anything to add with my structure meaning I could write that better or make that in another way I would really appriciate that, because I learn through it.

 

Yes when working with WPF you should be adhering to the MVVM design pattern. You'll find that you can't necessarily do everything you need to initially so once you hit that point you should consider augmenting the pattern by pulling in a framework such as MVVM Light (my personal favorite).

The single biggest problem in communication is the illusion that it has taken place.

Link to comment
Share on other sites

Link to post
Share on other sites

Yes you are right, you need to acquire the Dispatcher instance from the UI context and then from your thread (for which I recommend you use a Task) call either Invoke or BeginInvoke on it (depending on your needs) with your lambda. There's a more detailed explanation of the Dispatcher here and here. The latter touches on the SynchronizationContext of which there's only a implementation for the UI thread provided by default.

So if I am correct and understood you right I am doing it right, but why is it not working then might you be able to help me on that? And thanks for the links will check them out.

 

Would you mind giving me a brief story of MVVM like what it is? I know it is Model View ViewModel. But that doesn't help me alot. I did look into that like 2months ago but I didn't get anything that was my problem. D:

Link to comment
Share on other sites

Link to post
Share on other sites

So if I am correct and understood you right I am doing it right, but why is it not working then might you be able to help me on that? And thanks for the links will check them out.

 

It's not working because in your code you are calling Dispatcher from the context of a different thread, see this explanation for what's going on. As a side point you should use a Task instead of a Thread as the former offers a higher level of abstraction, proper cancellation, a managed thread pool and more.

 

So if I am correct and understood you right I am doing it right, but why is it not working then might you be able to help me on that? And thanks for the links will check them out.

 

Would you mind giving me a brief story of MVVM like what it is? I know it is Model View ViewModel. But that doesn't help me alot. I did look into that like 2months ago but I didn't get anything that was my problem. D:

 

Certainly. I know it can look rather complicated when you meet it for the first time but trust me it's rather simple once you have persevered with it. It probably doesn't help that, because by its very nature i.e. being a design pattern, it is open to interpretation thus there's absolutely tons of different takes on its implementation floating around the internet.

 

Let's take a visual/tactile approach first. Have some code to play with (this is a very basic implementation of the pattern):

 

Create a new WPF application and paste all of this in verbatim:

MainWindow.xaml

MainWindow.xaml.cs

MainWindowViewModel.cs

 

When we implement MVVM we are interested specifically in separation of concerns and encapsulation. We want our View to be just that, a UI and nothing more. It shouldn't have to know anything about the VIewModel (the business logic) and vice versa the VIewModel shouldn't need to know anything about the View. Then you have the Model which is usually where you have a database interaction layer or compute model or so on and again this should be kept separated by a clean and clearly defined interface - nothing should ever cross over between any of the layers.

 

Communication between layers is achieved by various abstraction patterns/mechanisms such as ViewModelLocators, Data Bindings, Dependency Injection, Inversion of Control, events, interaction requests, Dependency Properties, messages and others.

 

The pattern naturally promotes a clean and very testable design where one should also be able to swap any of the layers out at a later stage without needing to change any dependant code.

<Window x:Class="WpfApplication1.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:viewModel="clr-namespace:WpfApplication1"        mc:Ignorable="d"                Title="MainWindow" Height="150" Width="800">    <Window.DataContext>        <viewModel:MainWindowViewModel/>    </Window.DataContext>    <Grid>        <ProgressBar Minimum="{Binding Minimum}" Maximum="{Binding Maximum}" Value="{Binding Current}"/>        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Text}"/>    </Grid></Window> 
namespace WpfApplication1{    public partial class MainWindow    {        public MainWindow()        {            InitializeComponent();        }    }}
using System.ComponentModel;using System.Threading;using System.Threading.Tasks;namespace WpfApplication1{    internal class MainWindowViewModel : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        private string _text;        private int _current;        public MainWindowViewModel()        {            Text = string.Empty;            Task.Run(                () =>                    {                        var inc = 0;                        while (true)                        {                            if (Current <= 0)                            {                                inc = 1;                            }                            if (Current >= 100)                            {                                inc = -1;                            }                            Current += inc;                            Text = $"{Current}/{Maximum}";                            Thread.Sleep(50);                        }                    });        }        public string Text        {            get            {                return _text;            }            private set            {                _text = value;                OnPropertyChanged("Text");            }        }        public int Minimum { get; private set; }        public int Maximum { get; } = 100;        public int Current        {            get            {                return _current;            }            set            {                _current = value;                OnPropertyChanged("Current");            }        }        private void OnPropertyChanged(string propertyName = null)        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));        }    }}

The single biggest problem in communication is the illusion that it has taken place.

Link to comment
Share on other sites

Link to post
Share on other sites

 

 

 

It's not working because in your code you are calling Dispatcher from the context of a different thread, see this explanation for what's going on. As a side point you should use a Task instead of a Thread as the former offers a higher level of abstraction, proper cancellation, a managed thread pool and more.

 
 

 

Certainly. I know it can look rather complicated when you meet it for the first time but trust me it's rather simple once you have persevered with it. It probably doesn't help that, because by its very nature i.e. being a design pattern, it is open to interpretation thus there's absolutely tons of different takes on its implementation floating around the internet.

 

Let's take a visual/tactile approach first. Have some code to play with (this is a very basic implementation of the pattern):

 

Create a new WPF application and paste all of this in verbatim:

MainWindow.xaml

MainWindow.xaml.cs

MainWindowViewModel.cs

 

When we implement MVVM we are interested specifically in separation of concerns and encapsulation. We want our View to be just that, a UI and nothing more. It shouldn't have to know anything about the VIewModel (the business logic) and vice versa the VIewModel shouldn't need to know anything about the View. Then you have the Model which is usually where you have a database interaction layer or compute model or so on and again this should be kept separated by a clean and clearly defined interface - nothing should ever cross over between any of the layers.

 

Communication between layers is achieved by various abstraction patterns/mechanisms such as ViewModelLocators, Data Bindings, Dependency Injection, Inversion of Control, events, interaction requests, Dependency Properties, messages and others.

 

The pattern naturally promotes a clean and very testable design where one should also be able to swap any of the layers out at a later stage without needing to change any dependant code.

 

Thanks man for all the information and such. Really appriciate it. Will take a better look into MVVM and will start using it :D.

 

Thanks again

<Window x:Class="WpfApplication1.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:viewModel="clr-namespace:WpfApplication1"        mc:Ignorable="d"                Title="MainWindow" Height="150" Width="800">    <Window.DataContext>        <viewModel:MainWindowViewModel/>    </Window.DataContext>    <Grid>        <ProgressBar Minimum="{Binding Minimum}" Maximum="{Binding Maximum}" Value="{Binding Current}"/>        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Text}"/>    </Grid></Window> 
namespace WpfApplication1{    public partial class MainWindow    {        public MainWindow()        {            InitializeComponent();        }    }}
using System.ComponentModel;using System.Threading;using System.Threading.Tasks;namespace WpfApplication1{    internal class MainWindowViewModel : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        private string _text;        private int _current;        public MainWindowViewModel()        {            Text = string.Empty;            Task.Run(                () =>                    {                        var inc = 0;                        while (true)                        {                            if (Current <= 0)                            {                                inc = 1;                            }                            if (Current >= 100)                            {                                inc = -1;                            }                            Current += inc;                            Text = $"{Current}/{Maximum}";                            Thread.Sleep(50);                        }                    });        }        public string Text        {            get            {                return _text;            }            private set            {                _text = value;                OnPropertyChanged("Text");            }        }        public int Minimum { get; private set; }        public int Maximum { get; } = 100;        public int Current        {            get            {                return _current;            }            set            {                _current = value;                OnPropertyChanged("Current");            }        }        private void OnPropertyChanged(string propertyName = null)        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));        }    }}
Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×