Emulating a console with a WPF window - c#

Firstly, a disclaimer, what you're about to witness is my first bit of coding for almost 20 years. I'm new to C# and WPF, trying to get my head WPF is more than a challenge.
For the past month I've been working on a pet project console application, which has been performing well. I'm now trying to take it another step further by adding a modern GUI to the project.
I'd like to emulate a console (just the basic outpu functionality) by using a WPF textblock wrapped inside a scroller in a WPF window. You can see the original console application in action here to get a better idea of the kind of console output I'm trying to emulate. But I'm having a major problem with basic function calls, and I assume it's because I don't fully understand how WPF/C# work under the hood.
The Application starts in code via Main() like so:
class Program
{
public static ConsoleWindow MainConsole = new ConsoleWindow();
[STAThread]
static void Main(string[] args)
{
Application MyApplication = new Application();
MyApplication.Run(MainConsole);
// The following code does not work, it produces no output in the Textblock
MainConsole.WriteLine("Crystal Console");
MainConsole.WriteLine("Version: " + Properties.Settings.Default.BuildVersion);
MainConsole.WriteLine("Current Time: " + DateTime.Now);
MainConsole.WriteLine("Last Login: " + Properties.Settings.Default.dateLastLogin);
}
}
The problem is that the methods called don't seem to have any affect on the content of the textblock.
Although I'm about to give a lot of information just in case it's needed, the question itself is quite simple: Why does the Textblock update fine when taking content from a textbox control on the same window, but doesn't show any updates when the same method is called in Main() ?
For testing purposes the window has a few Textboxes that call the .WriteLine method inside the window, and THAT works, so I know there isn't a problem with the .WriteLine code, which you can see here:
public void WriteLine(string Message = null, string Sender = null)
{
_Console.AddElement(new ConsoleElement(Sender, Message + "\n"));
_Console.DisplayContent(ConsoleTextBlock);
ConsoleScroller.ScrollToEnd();
}
Here is the code for the console itself in case it's needed, the class "ConsoleElement" is essentially just a object that contains the messages to be displayed in the Textblock as well as the formatting for each one.
class ConsoleStream
{
IList<ConsoleElement> ConsoleElements = new List<ConsoleElement>();
public void AddElement(ConsoleElement NewElement)
{
if (NewElement.Sender == null) // Sender is System not user.
{
NewElement.Content = " " + NewElement.Content;
NewElement.Font = new FontFamily("Arial");
NewElement.FontSize = 12;
}
ConsoleElements.Add(NewElement);
}
public void ClearElements()
{
ConsoleElements.Clear();
}
public void DisplayContent(TextBlock sender)
{
sender.Text = null;
foreach (ConsoleElement Message in ConsoleElements)
{
//If message is a status update, i.e. has no sender, format it as a system message.
if (Message.Sender != null)
{
sender.Inlines.Add(new Run(Message.Sender + ": ") { Foreground = Message.SenderColour, FontFamily = Message.Font, FontSize = Message.FontSize });
}
//if message has a sender it's either the user or the AI. Format it as a user message.
if (Message.Sender != null) sender.Inlines.Add(new Run(Message.Content) { Foreground = Message.ContentColour, FontFamily = Message.Font, FontSize = Message.FontSize });
else sender.Inlines.Add(new Run(Message.Content) { Foreground = Message.SystemColour, FontFamily = Message.Font, FontSize = Message.FontSize });
}
}
}

MyApplication.Run(MainConsole); takes control of the thread, the code after it doesn't execute until after you close the window.
Move the code to the load (or init) method of your ConsoleWindow

Related

Adding events to rich textbox components

I'm trying to add events to a label that I create every time in a rich textbox. Here is the code below.
In my class MainWindowController I have:
public static Window MainWindow = new Windows.MainWindow();
public static Pages.MainPage MainPage = new Pages.MainPage();
public static Pages.LoginPage LoginPage = new Pages.LoginPage();
public static FlowDocument ChatDocument = new FlowDocument();
public static Paragraph ChatParagraph = new Paragraph();
And in another class I have this method and I call it each time the client receives an update status message through the network.
private static void HandleStatus(string MessageString)
{
string Sender = AuxiliaryClientWorker.GetElement(MessageString, "-U ", " -Content");
string Message = AuxiliaryClientWorker.GetElement(MessageString, "-Content ", ".");
MainWindowController.MainWindow.Dispatcher.Invoke(() =>
{
#region Status updater
System.Windows.Controls.Label StatusLabel = new System.Windows.Controls.Label();
StatusLabel.FontSize = 18;
StatusLabel.FontStyle = FontStyles.Oblique;
StatusLabel.FontStyle = FontStyles.Italic;
StatusLabel.Foreground = System.Windows.Media.Brushes.Black;
StatusLabel.Content = Sender + " has updated their status to " + Message;
StatusLabel.MouseEnter += StatusLabel_MouseEnter;
#endregion
MainWindowController.ChatParagraph.Inlines.Add(StatusLabel);
MainWindowController.ChatParagraph.Inlines.Add(Environment.NewLine);
MainWindowController.ChatDocument.Blocks.Add(MainWindowController.ChatParagraph);
MainWindowController.MainPage.ClientChatTextBox.Document = MainWindowController.ChatDocument;
});
}
private static void StatusLabel_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
MessageBox.Show("This");
}
Everything works fine, the only problem is that the UI elements within the rich textbox do not "listen" for events, henceforth the events never fire. The MessageBox never shows. I tried adding the Dispatcher.Invoke command to the method below and it just refuses to work and the event never fires. I hope you all understand what I'm trying to do!
TLDR: StatusLabel_MouseEnter never fires
You need to set the IsDocumentEnabled property to true on your RichTextBox.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.richtextbox.isdocumentenabled?view=netcore-3.1
Gets or sets a value that indicates whether the user can interact with UIElement and ContentElement objects within the RichTextBox.
Events from a richtextbox document are kind of tricky.
You might also have to put a control in a BlockUIContainer for some events.
Even then, some things won't quite work as you might expect.
Clicking already has meaning so you will likely find it already handling click events. Use preview events - PreviewMouseDown and PreviewMouseUp for that sort of thing.

Unable to handle a button click event in SAP B1 UI API

I'm starting with SAP B1 UI API (9.0) and I'm trying to handle a button click without any luck so far. This is how I'm doing it (removing unnecessary to make it shorter):
static void Main(string[] args)
{
SetApplication(args);
var cParams = (FormCreationParams)App.CreateObject(BoCreatableObjectType.cot_FormCreationParams);
cParams.UniqueID = "MainForm_";
cParams.BorderStyle = BoFormBorderStyle.fbs_Sizable;
_form = App.Forms.AddEx(cParams);
/*Setting form's title, left, top, width and height*/
// Button
var item = _form.Items.Add("BtnClickMe", BoFormItemTypes.it_BUTTON);
/*Setting button's left, top, width and height*/
var btn = (Button)item.Specific;
btn.Caption = "Click Me";
_form.VisibleEx = true;
App.ItemEvent += new _IApplicationEvents_ItemEventEventHandler(App_ItemEvent);
}
private static void SetApplication(string[] args)
{
string connectionString = args[0];
int appId = -1;
try
{
var guiApi = new SboGuiApi();
guiApi.Connect(connectionString);
App = guiApi.GetApplication(appId);
}
catch (Exception e)
{ /*Notify error and exit*/ }
}
private static void App_ItemEvent(string FormUID, ref ItemEvent pVal, out bool BubbleEvent)
{
BubbleEvent = true;
if (FormUID == "MainForm_" && pVal.EventType == BoEventTypes.et_CLICK &&
pVal.BeforeAction && pVal.ItemUID == "BtnClickMe")
{
App.MessageBox("You just click on me!");
}
}
When I click the button nothing happens, is this the way to go? I've made so many variations in the handler method but nothing yet. Another detail is that the visual studio's debugger terminates as soon as the addon is started (maybe this has something to do with my problem).
I hope you can help me. Thanks in advance.
David.
Since the application stops running there are two possible answers to this question depending on what you prefer to use.
If you are using the SAPbouiCOM library you need a way to keep the application running, the way I use is the System.Windows.Forms.Application.Run(); from the windows forms assembly.
If you are using the SAPBusinessOneSDK and SAPbouiCOM.Framework as a reference you can use the App.Run();.
Both of these need to be invoked as soon as your setup code has run.

Changing textbox from another class

I am trying to change textbox data on my program, but it isn't working.
The form is already shown but when I try it doesn't update.
I'm trying to use form1.show(); but my program is using multithreading and it spams windows in every thread.
What I tried so far is this.
Form1.CS code
private delegate void NameCallBack(string varText);
public void UpdateTextBox(string input)
{
if (InvokeRequired)
{
paidbox.BeginInvoke(new NameCallBack(UpdateTextBox), new object[] { input });
}
else
{
paidbox.Text = paidbox.Text + input;
// textBox.Text = textBox.Text + Environment.NewLine + input // This might work as append in next line but haven't tested so not sure
}
}
On my Program.CS I used on every bool I want it to be updated.
public static Form1 MainLogWindow = new Form1();
Cheker.MainLogWindow.UpdateTextBox("test test");
This isn't working, it compiles successfully but it doesn't update when the process start the text on textbox.
Sorry for my bad english.
P.S I'm new on C#, I know only php and javascript so this is so new to me.

Let user start typing when winform pops up without them having to click on the textbox

I am working on a barcode reader project in Visual Studio using C#. I have created a WinForm Application and have added a RichTextBox to it. I want the user to be able to start scanning when they open the program without having to click on the textbox.
Thanks in advance!
(I'm assuming you have an application with a multitude of stuff in it. However there is one field that needs to be filled in with a scanned barcode.)
I faced a simular issue a while ago. I needed to capture a barcode in WPF. Setting the focus property in load seemed a good idea but because there were a multitude of other controls on the page that the user could click etc. focus jumped from one control to the other, making the barcode go in the wrong fields or vanish in a grid that has focus for example.
We were not able to use any other way of reading the barcode from the scanner because it was used for other applications too. It had to be configured as input.
We came up with a solution of capturing the keypresses instead.
By using the keydown events we could track the scanner input and stated that if more than 5 keys came in within a limited time + with our prefix and suffix it had to be a barcode.
EDIT: here is a simplified version of the class.
public delegate void BarcodeRead(string barcode);
public class ManualReader
{
private string barcode = "no barcode detected";
private string possible = "";
private DateTime timestarted = DateTime.MinValue;
private Timer InputTimeout;
public BarcodeRead OnBarcodeRead;
public void OnKeyDown(object sender, KeyEventArgs.KeyEventArgs e)
{
//create timer if it does not exist
if (InputTimeout == null)
{
InputTimeout = new Timer(100);
InputTimeout.Enabled = true;
InputTimeout.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
//reset timer
InputTimeout.Stop();
InputTimeout.Start();
//possible barcode
possible += CharToKey.GetCharFromKey(e);
if (timestarted == DateTime.MinValue)
{
timestarted = DateTime.Now;
}
}
//Timer elapses
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
//Is it a barcode?
if ((timestarted.AddMilliseconds(600) > DateTime.Now) && (possible.Length > 5)
&& (timestarted != DateTime.MinValue) && possible.Contains("\r"))
{
barcode = possible;
barcode = barcode.Remove(0, 1);
barcode = barcode.Replace("\r", "");
//launch delegate
if (OnBarcodeRead != null)
{
OnBarcodeRead.Invoke(barcode);
}
}
//delete timers
timestarted = DateTime.MinValue;
InputTimeout.Dispose();
InputTimeout = null;
possible = null;
}
}
}
I'm aware that for really short timeouts datetime functions aren't precise but still this little 'hack' worked perfectly for our application.
You can add directly in the element. This works for textbox but not sure with RichTexBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"

WPF window force redraw window size

I know there are lots of article over Google related to this, and believe me I have tried almost everything.
So the problem is, I have a wpf application in which when user focuses on any input control like textbox I am showing external touch keyboard using TabTip.exe.
Following code:
public class KeyboardHelper
{
private const string PROCESS_NAME = "TabTip";
private const string PROCESS_PATH = "Common Files/Microsoft Shared/ink/TabTip.exe";
public static void ShowKeyboard()
{
Process keyboard = null;
Process[] pname = Process.GetProcessesByName(PROCESS_NAME);
if (pname.Length == 0)
{
keyboard = new Process();
}
else
{
keyboard = pname[0];
}
string processPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), PROCESS_PATH);
keyboard.StartInfo.FileName = processPath;
keyboard.Start();
}
public static void Closekeyboard()
{
Process[] pname = Process.GetProcessesByName(PROCESS_NAME);
if (pname.Length > 0)
{
pname[0].Kill();
}
}
}
Now problem starts here keyboard open but if it is in docked mode, it causes my app to resize almost half of the screen. And when I close the keyboard app remains same in size, I want to restore to full screen state again.
Controlling my size from cs because it comes from a DB
WindowState = maximized;
ResizeMode= NoResize;
WindowsStyle = None;
Any work around to solve the issue.
I have tried UpdateLayout, Dispatcher.BeginInvoke, Invalidate methods.
Thanks Jason, now I know the cause or can say case scenario of the problem. In my app I have to decide the startup page at runtime, So I am using code like this
private void Application_Startup(object sender, StartupEventArgs e)
{
//Set startup page for the application.
MainWindow window = new MainWindow();
window.ShowDialog();
}
And this is the problem. If I use normal default way than everything works fine.

Categories