WPF: polling the keyboard - c#

I am working on a WPF application that runs on an embedded device (.NET Standard 4 for embedded). It has a whole bunch of hardware attached which gets in the way when I'm testing, so I created a DummyHardware interface that just does nothing except print log messages when I run my unit tests, or run stand-alone on my development PC.
So far so good. But: the device has a 4-key keypad which is polled. My dummy keypad class went into an infinite loop when waiting for a key to be pressed, because there were no keys to press :-) So I thought, "Ok, I'll poll the keyboard to see if 1,2,3 or 4 is pressed". But I get the exception
The calling thread must be STA...
when I called Keyboard.IsKeyDown( Key.D1 ). The keypad polling takes place in a separate thread (to decouple from the generally slow serial comms of the rest of the hardware). Any thoughts on how to proceed? Invoke?
Note: one alternative would be to just skip the "wait for key" test on dummy hardware, but then I don't know which key was pressed and the following code which relies on it won't function correctly. Yuk.

You can just set the ApartmentState to be STA. using the Thread.SetApartmentState method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
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;
namespace staThread
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Thread keyboardThread;
public MainWindow()
{
InitializeComponent();
keyboardThread = new Thread(new ThreadStart(KeyboardThread));
keyboardThread.SetApartmentState(ApartmentState.STA);
keyboardThread.Start();
}
void KeyboardThread()
{
while (true)
{
if (Keyboard.IsKeyDown(Key.A))
{
}
Thread.Sleep(100);
}
}
}
}

I have a simple method that handles running on the UI thread for me:
public object RunOnUiThread(Delegate method)
{
return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}
Where Dispatcher is initialised using Dispatcher.CurrentDispatcher from the UI thread. It can be called from any thread and is used like this:
UiThreadManager.RunOnUiThread((Action)delegate
{
// running on the UI thread
});

Related

Change window location of process started with c#

I'm making a c# console .net framework application to open the file Error.vbs, and I want to be able to choose the location of the started file's window on the desktop.
This is the code I have so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ErrorRunner
{
internal class Program
{
static void Main(string[] args)
{
Process ExternalProcess = new Process();
ExternalProcess.StartInfo.FileName = #"C:\Users\video\Downloads\Error.vbs";
while (true)
{
ExternalProcess.Start();
}
}
}
}
How would I choose the start location of error.vbs?
(And yes, I know it might be a bad idea to start a process indefinitely, but that's the whole purpose of this program.)
You can retrieve the external process window handle by using ExternalProcess.MainWindowHandle. Maybe you need to wait for process to start completely (see https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.mainwindowhandle?view=net-6.0#System_Diagnostics_Process_MainWindowHandle)
So you can use the handle with a P/Invoke to SetWindowPos (see https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos)

WPF application without loading a window automatically issue

I have set up a WPF application so that it does not load a window automatically by modifying the app.xaml.cs as shown below and by setting the build action to Page. I have also removed the starturl from the app.xaml. I have introduced a controller class from where the application starts as you can see in the code below and in the constructor for the controller I have created and opened a new window. I deleted the original MainWindow which was produced automatically when opening the application.
Anyway the problem is that after opening the new window that I have added in the controller I then close the window and the Application terminates? Why? I don't want the application to terminate and I don't understand why it is terminating on closing the window. Or to put it another way how can I introduce a window that doesn't cause the application to terminate on closing the window? Any help greatly appreciated.
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace UserTraining
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
App()
{
InitializeComponent();
}
[STAThread]
static void Main()
{
App app = new App();
UserTrainingController control = new UserTrainingController();
app.Run();
}
}
}
UserTrainingController class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace UserTraining
{
class UserTrainingController
{
public UserTrainingController()
{
IntroductionWindow w = new IntroductionWindow();
w.Show();
}
}
}
A WPF application has a ShutdownMode property, whose value is one of the members of the ShutdownMode enumeration. This has three possible values: OnExplicitShutdown, OnLastWindowClose and OnMainWindowClose.
The default is set to OnLastWindowClose which means that the application will shutdown when either the last window closes, or Application.Shutdown() is called. I suspect this is why your application terminates when your IntroductionWindow is closed.
What you need to do is set ShutdownMode to OnExplicitShutdown instead. That way, your application will only terminate when you explicitly call Application.Shutdown().
You can set the ShutdownMode property either in code or in your App.xaml file.
After UserTrainingController is called, you get the window shown in the screen with w.Show();. When you close the window, w.Show(); terminates and comes back to the caller, which is Main method. It's the last method on the stack.
But then you run another method, app.Run(), but it's terminates immediately, returning to Main method, which after app.Run() has nothing more to do, so it terminates as well.
Since it was last method on the stack, stack is empty, meaning that your application finished.
You need to have some parent window, from which you can open your custom window.
Generally, you have to prevent stack from emptying :)

How do I make an infinite loop in C# Windows Form Application

I have written a C# program that should loop the code to refresh the DateTime info and set the text of a label to match. However, when I run it, the form doesn't display. Why does this happen, and how do I make the form be shown?
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Clock
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new clock());
}
}
}
Ignoring my indentions (which may or may not be right, but are correct in the program), what am I doing wrong, and how do I fix it?
Thanks!
Logan Slowik
Seems like you are blocking the UI thread with your infinite loop. You can utilize async to avoid blocking your UI... Just call this method in your Form_Load method. InfiniteLoop();
async void InfiniteLoop()
{
while(true)
{
await Task.Delay(100);
this.Text = DateTime.Now.ToString();
}
}
You may also consider using Timer

C# VS2015 Changing from Forms to Universal Apps: Missing assembly error

I am writing a little program to control LEGO Mindstorms car using monobrick C# library from Monobrick.dk. I have used the library in Windows Forms and it worked. As I heard Forms are outdated way to write Windows apps so I would like to switch to Windows Universal Platform. I made a little code similar to the working one but I get error:
The type or namespace name 'Thread' does not exist in the namespace 'System.Threading' (are you missing an assembly reference?) App1 \App1\MainPage.xaml.cs 37
It is strange cause in Forms "System.Threading.Thread" worked.
Here is the code. It is supposed to turn Motor A in half power for 3 secs. after pushing an on screen button.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using MonoBrick.EV3;
namespace App1
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
var ev3 = new Brick<Sensor, Sensor, Sensor, Sensor>("wifi");
ev3.Connection.Open();
ev3.MotorA.On(50);
System.Threading.Thread.Sleep(3000);
ev3.MotorA.Off();
}
}
}
What am I missing here?
Isn't WUP a little overkill for beginner? Maybe I should stick to WPF?
Will be grateful for your advice.
The System.Threading.Thread has been removed from the Windows Runtime (which powers the Universal Windows Platform). Instead, Microsoft wants you to focus on asynchronous programming patterns instead of using the Thread class directly (see this page for the asynchronous programming pattern documentation).
As answered in this question you should use the Task.Delay method, so your code would become something along the following lines:
private async void button_Click(object sender, RoutedEventArgs e)
{
// code before
await Task.Delay(3000);
// code after
}
This has the added benefit that you UI would not become unresponsive, as the Thread.Sleep method would freeze the UI thread if it was not executed in another thread or asynchronous call.
You also might want to read this article about asynchronous programming in C# and VB.NET.

Console.Write in .NET GUI Application

In a C# .NET GUI Application. I also need the console in the background for some tasks. Basically, I'm using a Thrid Party library for some processing (takes lot of time) which writes its intermediate results to console. This processing is a computationally time taking task. So, I'm assigning this task to backgroundworker. I mean background worker calls these library functions. But problem is there is no way for me to show the user status of computation, because I don't have source of the library. I was hoping Console will be shown. But surprisingly Console.WriteLine doesn't seem to work. I mean, there isn't any console window shown. How come?
EDIT:
I tried setting application type = console. But there seems to be a problem. Only, main thread is able to access the console. Only Console.WriteLines executed by main (Application) thread are displayed on console. Console.WriteLines executed by other (BackgroundWorker)threads of the GUI, the output is not shown. I need console only for Background workers. I mean, When background worker starts, console starts & when it ends console will be off.
Create your own console window and use the Console.SetOut(myTextWriter); method to read anything written to the console.
Set your application type to "Console Application". Console applications can also create GUI windows with no problem, and write to the console at the same time.
If you don't have control of the main application, and you want to make sure that a console is shown, you can p/invoke AllocConsole (signature here).
This isn't the same as being a console application though, your application will always get a separate console window, which might be surprising to someone who launched it from a command prompt window. You can work around that with AttachConsole (signature and example here) but shell redirection of the output still won't work. That's why I suggest setting the application subsystem to console if you can.
Followed by #jgauffin, here is the implementation of Console.SetOut method.
Create a TextWriter inherited class.
using System;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace ConsoleRedirection
{
public class TextBoxStreamWriter : TextWriter
{
TextBox _output = null;
public TextBoxStreamWriter(TextBox output)
{
_output = output;
}
public override void Write(char value)
{
base.Write(value);
_output.AppendText(value.ToString()); // When character data is written, append it to the text box.
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
}
And in the Form, code as below.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace ConsoleRedirection
{
public partial class FormConsole : Form
{
// That's our custom TextWriter class
TextWriter _writer = null;
public FormConsole()
{
InitializeComponent();
}
private void FormConsole_Load(object sender, EventArgs e)
{
// Instantiate the writer
_writer = new TextBoxStreamWriter(txtConsole);
// Redirect the out Console stream
Console.SetOut(_writer);
Console.WriteLine("Now redirecting output to the text box");
}
// This is called when the "Say Hello" button is clicked
private void txtSayHello_Click(object sender, EventArgs e)
{
// Writing to the Console now causes the text to be displayed in the text box.
Console.WriteLine("Hello world");
}
}
}
Original code is from https://saezndaree.wordpress.com/2009/03/29/how-to-redirect-the-consoles-output-to-a-textbox-in-c/
You can check the link for cross-thread calls and advanced implementations at comments.

Categories