How to get the name of the current and calling function in dart? - web

C# has:
System.Reflection.MethodBase.GetCurrentMethod().Name
Does Dart have something similar but returns results for both the function that is currently being run as well as the name of the function that called the currently run function.

I wrote a simple class that gives the current function and the caller function, but also, the file name, line number and column line from the StackTrace.current property.
Heres the code:
class CustomTrace {
final StackTrace _trace;
String fileName;
String functionName;
String callerFunctionName;
int lineNumber;
int columnNumber;
CustomTrace(this._trace) {
_parseTrace();
}
String _getFunctionNameFromFrame(String frame) {
/* Just giving another nickname to the frame */
var currentTrace = frame;
/* To get rid off the #number thing, get the index of the first whitespace */
var indexOfWhiteSpace = currentTrace.indexOf(' ');
/* Create a substring from the first whitespace index till the end of the string */
var subStr = currentTrace.substring(indexOfWhiteSpace);
/* Grab the function name using reg expr */
var indexOfFunction = subStr.indexOf(RegExp(r'[A-Za-z0-9]'));
/* Create a new substring from the function name index till the end of string */
subStr = subStr.substring(indexOfFunction);
indexOfWhiteSpace = subStr.indexOf(' ');
/* Create a new substring from start to the first index of a whitespace. This substring gives us the function name */
subStr = subStr.substring(0, indexOfWhiteSpace);
return subStr;
}
void _parseTrace() {
/* The trace comes with multiple lines of strings, (each line is also known as a frame), so split the trace's string by lines to get all the frames */
var frames = this._trace.toString().split("\n");
/* The first frame is the current function */
this.functionName = _getFunctionNameFromFrame(frames[0]);
/* The second frame is the caller function */
this.callerFunctionName = _getFunctionNameFromFrame(frames[1]);
/* The first frame has all the information we need */
var traceString = frames[0];
/* Search through the string and find the index of the file name by looking for the '.dart' regex */
var indexOfFileName = traceString.indexOf(RegExp(r'[A-Za-z]+.dart'));
var fileInfo = traceString.substring(indexOfFileName);
var listOfInfos = fileInfo.split(":");
/* Splitting fileInfo by the character ":" separates the file name, the line number and the column counter nicely.
Example: main.dart:5:12
To get the file name, we split with ":" and get the first index
To get the line number, we would have to get the second index
To get the column number, we would have to get the third index
*/
this.fileName = listOfInfos[0];
this.lineNumber = int.parse(listOfInfos[1]);
var columnStr = listOfInfos[2];
columnStr = columnStr.replaceFirst(")", "");
this.columnNumber = int.parse(columnStr);
}
}
This class takes in a StackTrace object and reads its string and parse it.
How to use it (get the info):
void main() {
CustomTrace programInfo = CustomTrace(StackTrace.current);
print("Source file: ${programInfo.fileName}, function: ${programInfo.functionName}, caller function: ${programInfo.callerFunctionName}, current line of code since the instanciation/creation of the custom trace object: ${programInfo.lineNumber}, even the column(yay!): ${programInfo.columnNumber}");
}
The variable programInfo now has the function name, the caller function name, line number, column number and even the file name of the current program's execution.
You can print to the console the following:
print(StackTrace.current.toString());
And you will see how the string looks and be able to understand how i parse the string in order to get the information.
The simple benefit of this is that you dont have to install any library. I made this because i was doing a project just using Dart and i didnt want to add/install any third party library into my simple project. And you will end up with an object having all of the information by just calling the constructor. The downside of this is that it if Dart, for some reason, changes the string format of the stack trace somewhere in the future, this will no longer work BUT if this somehow happens, you can easily change how this class parses the frames*/
NOTE: This code by no means is the most optimize code, but it works :D. I would like to see some better implementations and abstractions.

There is no way to directly access the call stack in the Dart reflection library.
You can get a string representation of the stack trace, and then try to parse that:
var stack = StackTrace.current;
var stackString = "$stack"; // because the only method on StackTrace is toString.
The stack_trace package tries to do this for you for a number of known stack trace formats, so maybe:
import "package:stack_trace";
main() {
print(Trace.current().frames[0].member); // prints "main" unless minified.
}

Tidied up #LuisDev99's answer a bit, optimizing for yourself:
class LoggerStackTrace {
const LoggerStackTrace._({
required this.functionName,
required this.callerFunctionName,
required this.fileName,
required this.lineNumber,
required this.columnNumber,
});
factory LoggerStackTrace.from(StackTrace trace) {
final frames = trace.toString().split('\n');
final functionName = _getFunctionNameFromFrame(frames[0]);
final callerFunctionName = _getFunctionNameFromFrame(frames[1]);
final fileInfo = _getFileInfoFromFrame(frames[0]);
return LoggerStackTrace._(
functionName: functionName,
callerFunctionName: callerFunctionName,
fileName: fileInfo[0],
lineNumber: int.parse(fileInfo[1]),
columnNumber: int.parse(fileInfo[2].replaceFirst(')', '')),
);
}
final String functionName;
final String callerFunctionName;
final String fileName;
final int lineNumber;
final int columnNumber;
static List<String> _getFileInfoFromFrame(String trace) {
final indexOfFileName = trace.indexOf(RegExp('[A-Za-z]+.dart'));
final fileInfo = trace.substring(indexOfFileName);
return fileInfo.split(':');
}
static String _getFunctionNameFromFrame(String trace) {
final indexOfWhiteSpace = trace.indexOf(' ');
final subStr = trace.substring(indexOfWhiteSpace);
final indexOfFunction = subStr.indexOf(RegExp('[A-Za-z0-9]'));
return subStr
.substring(indexOfFunction)
.substring(0, subStr.substring(indexOfFunction).indexOf(' '));
}
#override
String toString() {
return 'LoggerStackTrace('
'functionName: $functionName, '
'callerFunctionName: $callerFunctionName, '
'fileName: $fileName, '
'lineNumber: $lineNumber, '
'columnNumber: $columnNumber)';
}
}
print(LoggerStackTrace.from(StackTrace.current).toString());

import 'dart:mirrors';
...
MethodMirror methodMirror = reflect(functionOne).function;
See also https://github.com/dart-lang/sdk/issues/11916#issuecomment-108381556
This will only work in the Dart command line VM, but not in the browser or Flutter because there reflection is not supported.
Code generation solutions like https://pub.dartlang.org/packages/reflectable might work instead where reflection is not available.
https://github.com/dart-lang/sdk/issues/28372 seems related.

LuisDev99's answer doesn't cope well with inner methods and anonymous lambda blocks, so I used a more complex regex approach.
My solution:
/*
Define regex for each entry in the stack
group 0: full line
group 1: stack index
group 2: function name
group 3: package
group 4: file name
group 5: line number
group 6: column number
*/
RegExp regExp = new RegExp(r'^#(\d+) +(.+) +\(package:([^/]+)/(.+\.\w):(\d+):(\d+)\)$');
/* Get the stack as an array of strings */
var frames = StackTrace.current.toString().split("\n");
/* The second entry in the stack is the caller function */
var matches = regExp.allMatches(frames[1])
/* The regex matches each line of the stack only once so only one match */
var match = matches.elementAt(0);
/* Print all groups. Note that "groupCount" doesn't include group 0 (the whole line) */
for (int i = 0; i <= match.groupCount; i++) {
print("group $i: " + match.group(i));
}

Related

duktape how to parse argument of type String Object (similarly Number object) in duktape c function

How to type check the String object/Number object argument types in duktape c function and parse the value from String object/Number object. There is generic api like duk_is_object() but I need the correct object type to parse the value .
ex:
ecmascript code
var str1 = new String("duktape");
var version = new Number(2.2);
dukFunPrintArgs(str1,str2);
duktape c function :
dukFunPrintArgs(ctx)
{
// code to know whether the args is of type String Object / Number Object
}
Where did you find the information how to register a C function in duktape? That place certainly also has details on how to access the parameters passed to it. Already on the homepage of duktape.org you can find a Getting Started example:
3 Add C function bindings
To call a C function from Ecmascript code, first declare your C functions:
/* Being an embeddable engine, Duktape doesn't provide I/O
* bindings by default. Here's a simple one argument print()
* function.
*/
static duk_ret_t native_print(duk_context *ctx) {
printf("%s\n", duk_to_string(ctx, 0));
return 0; /* no return value (= undefined) */
}
/* Adder: add argument values. */
static duk_ret_t native_adder(duk_context *ctx) {
int i;
int n = duk_get_top(ctx); /* #args */
double res = 0.0;
for (i = 0; i < n; i++) {
res += duk_to_number(ctx, i);
}
duk_push_number(ctx, res);
return 1; /* one return value */
}
Register your functions e.g. into the global object:
duk_push_c_function(ctx, native_print, 1 /*nargs*/);
duk_put_global_string(ctx, "print");
duk_push_c_function(ctx, native_adder, DUK_VARARGS);
duk_put_global_string(ctx, "adder");
You can then call your function from Ecmascript code:
duk_eval_string_noresult(ctx, "print('2+3=' + adder(2, 3));");
One of the core concepts in duktape are stacks. The value stack is where parameters are stored. Read more on the Getting Started page.

Need help understand how Strings work in my method

I am trying to understand how this convertingStringToInt method works. I am reading a file, storing the values in an array and am to pass those values to the method to be converted. In the parameters of convertingStringToInt, I have (String number) I don't get where the String "number" is getting its values. So I am passing in a string called numbers, but how is that newly created String associated with any of the values in my file...?!?
I am trying to understand the cause all the return numbers are the error code -460 except the last digit in the file. So the String numbers is associated with the file somehow I just don't get how...
public static void read_file()
{
try {
File file = new File("randomNumbers.txt");
Scanner scan = new Scanner(file);
int amountOfNumbersInFile = convertingStringToInt(scan.nextLine()); // read the first line which is 100 to set array size
global_numbers = new int[amountOfNumbersInFile]; // set the array size equal to the first line read which is 100
for (int index = 0; index < amountOfNumbersInFile; index++)
{
String line = scan.nextLine();
global_numbers [index] = convertingStringToInt(line);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static int convertingStringToInt(String numbers) //what does string "number" equal? why/where is it declared?
{
String numbers = scan.nextInt();
try {
return Integer.parseInt(numbers);
} catch (NumberFormatException n) {
return -460;
}
}
I have global_numbers declared as a global variable.
so the first thing u need understand is what u have in your txt file
if in this file you have only number is ok use stringToInt
but if you have words this never work properly

Getting Values out of a String with patterns

I have textfiles, which have attributes saved in Strings. Those Strings have a pattern like this:
[attributeName]:[value]
I can't generalize the [value], because it could be of any primitive datatype.
Saving the effectively values is not my concern, because it's depending on the user which attribute has to be loaded. The same file won't be loaded very often.
Now I have 2 problems:
1) For some reason the program which creates those files sometimes adds spaces around the : at some attributes and [value] could also contain spaces, so I have to get rid of those
2) Making the reading of those attributes more performant:
I've come up with this method:
public String getAttribute(File file, String attribute)
{
try
{
BufferedReader reader = new BufferedReader(new FileReader(file), 1024);
String line;
Pattern p = Pattern.compile(Pattern.quote(attribute), Pattern.CASE_INSENSITIVE);
while ((line = reader.readLine()) != null)
{
int i = line.indexOf(":");
if(line.charAt(i-1) == ' ')
line = line.substring(0,i-2) + line.substring(i);
if(line.charAt(i+1) == ' ')
line = line.substring(0,i) + line.substring(i+2);
if (p.matcher(line).find())
{
return line.replace(attribute, "").trim();
}
}
} catch (IOException e)
{
e.printStackTrace();
}
return null;
}
However, this method will probably be one of the most called by my application, so I can't leave it so unperformant as it is right now,
Thanks for any help!
I modified code to find appropriate line. Check example code below.
If you have a lot of files and attributes in these files you could think about saving somewhere pair attribute=value in code. In example code I provided very primitive cache by using Table interface from guava library.
Example code:
# guava library
import com.google.common.collect.Table;
import com.google.common.collect.HashBasedTable;
# apache commons lang
import static org.apache.commons.lang.StringUtils.startsWithIgnoreCase;
# apache commons io
import static org.apache.commons.io.IOUtils.closeQuietly;
[...]
# very primitive cache implementation. To find a value in table you have to
# pass row and column keys. In this implementation row is equal to file
# absolute path (because you can have 2 files with the same name) and column
# is equal to attribute name.
# If you have a lot of files and attributes probably you will have to clear
# from time to time the cache otherwise you will get out of memory
private static final Table<String, String, String> CACHE = HashBasedTable.create();
[...]
public String getAttribute(File file, String attribute) {
# get value for the given attribute from the given file
String value = CACHE.get(file.getAbsolutePath(), attribute);
# if cache does not contain value, method will read value from file
if (null == value) {
BufferedReader reader = null;
String value = null;
try {
reader = new BufferedReader(new FileReader(file), 1024);
String line;
while ((line = reader.readLine()) != null) {
# From your description I understood that each line in file
# starts with attribute name
if (startsWithIgnoreCase(line, attribute) {
# if we found correct line we simple split it by ':'
String[] array = line.split(":");
# this is to ensure that line contains attribute name
# and value
if (array.length >= 2) {
# we found value for attribute and we remove spaces
value = array[1].trim();
# we put value to the cache to speed up finding
# value for the same attribute in the future
CACHE.put(file.getAbsolutePath(), attribute, value);
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
# you should always close
closeQuietly(reader);
}
}
return value;
}

Automate Resharper's "to property with backing field" across many properties and classes?

I've got some C# code like this (class file = Status.cs):
/// <summary>
/// Constructs a status entity with the text specified
/// </summary>
/// <param name="someParameter">Some parameter.</param>
public Status(string someParameter)
{
SomeProperty = someParameter;
}
/// <summary>
/// An example of a virtual property.
/// </summary>
public virtual string SomeProperty { get; private set; }
And I want to do 3 things to it:
perform a Resharper "to property with backing field" on it
get rid of the "private set" and replace it with a regular "set"
change the constructor so it initializes the private field instead of the property
So the end result would look like this:
/// <summary>
/// Constructs a status entity with the text specified
/// </summary>
/// <param name="someParameter">Some parameter.</param>
public Status(string someParameter)
{
_someProperty = someParameter;
}
private string _someProperty;
/// <summary>
/// An example of a virtual property.
/// </summary>
public virtual string SomeProperty
{
get { return _someProperty; }
set { _someProperty = value; }
}
And my question is: Is there a way to automate this type of refactoring using, say, the Resharper API ?
Background:
For those who might wonder why I want to do this, it's because:
I'm upgrading NHibernate (old=2.1.2.4000, new=3.3.1.4000) and Fluent NHibernate (old=1.1.0.685, new=1.3.0.0).
I've got rid of the old NHibernate.ByteCode.Castle.dll and the corresponding line in the config file, so I can now use the default proxy that's built into the latest NHibernate.
Between the new implementation of NHibernate and the new version of Fluent, there seem to be two problems when I try to build and run unit tests (part of this is FxCop complaining, but anyway):
a) exceptions are thrown because of the "private set", and
b) exceptions are thrown because the virtual property is being initialized in the constructor.
So I found that if I make those changes, it compiles and the unit tests pass.
So that's fine for a class or two, but there are over 800 class files and who knows how many properties.
I'm sure there are lots of ways to do this (e.g. using reflection, recursing through the directories and parsing the files etc), but it seems like Resharper is the proper tool for something like this.
Any help appreciated, thank you, -Dave
--In response to the answer saying "just change it to a protected set and you're done":
Unfortunately it’s not that simple.
Here is the error that occurs on running unit tests (before making any changes to any class):
Test method threw exception:
NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
.Status: method set_StatusText should be 'public/protected virtual' or 'protected internal virtual'
..Status: method set_Location should be 'public/protected virtual' or 'protected internal virtual'
So if I change the class as suggested (where the only change is to change the “private set” to a “protected set”), the project will not build, because:
Error 2 CA2214 : Microsoft.Usage : 'Status.Status(string, StatusLocation)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences:
Status..ctor(String, StatusLocation)
Status.set_Location(StatusLocation):Void C:\\Status.cs 28
That is why it is also necessary to change any statement in the constructor which initializes one of these virtual properties.
The previous implementation of the NHibernate proxy (ByteCode.Castle) did not seem to care about the “private set”, whereas this one does.
And, admittedly, the second error is because of FxCop, and I could just put an attribute on the class to tell FxCop not to complain about this, but that seems to just be making the problem go away, and initializing virtual properties in constructors is, as I understand it, bad practice anyway.
So my question still stands. The changes I initially described are the changes I would like to make. How can I automate these changes?
I went ahead and wrote a C# utility to parse such a class file. The "protected set" idea is valid to a point (thanks Hazzik), but it still needs a backing field. The below code produces the output I described above (except uses a "protected set"). Regards, -Dave
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
// TODO: write recursive algorithm to loop through directories
// TODO: handle generic collections as Fluent NHibernate treats those differently
class Program
{
public static string ConvertInitialCapitalToUnderscoreAndLowerCase(string input)
{
var firstChar = input.Substring(0, 1);
var restOfStmt = input.Substring(1);
var newFirst = "_" + firstChar.ToLower();
var output = newFirst + restOfStmt;
return output;
}
// this gets any tabs or spaces at the beginning of the line of code as a string,
// so as to preserve the indentation (and/or add deeper levels of indentation)
public static string GetCodeLineIndentation(string input)
{
var charArray = input.ToCharArray();
var sbPrefix = new StringBuilder();
foreach (var c in charArray)
{
// if it's a tab or a space, add it to the "prefix"
if (c == 9 || c == ' ')
{
sbPrefix.Append(c);
}
else
{
// get out as soon as we hit the first ascii character (those with a value up to 127)
break;
}
}
return sbPrefix.ToString();
}
static void Main(string[] args)
{
const string path = #"C:\pathToFile\Status.cs";
Console.WriteLine("Examining file: " + path);
if (!File.Exists(path))
{
Console.WriteLine("File does not exist: " + path);
throw new FileNotFoundException(path);
}
// Read the file.
var arrayOfLines = File.ReadAllLines(path);
// Convert to List<string>
var inputFileAsListOfStrings = new List<string>(arrayOfLines);
// See if there are any virtual properties.
var virtualProps = inputFileAsListOfStrings.Where(s => s.Contains("public virtual")).ToList();
// See if there are any "private set" strings.
var privateSets = inputFileAsListOfStrings.Where(s => s.Contains("private set")).ToList();
if (virtualProps.Count > 0)
{
Console.WriteLine("Found " + virtualProps.Count + " virtual properties in the class...");
}
if (privateSets.Count > 0)
{
Console.WriteLine("Found " + privateSets.Count + " private set statements in the class...");
}
// Get a list of names of the virtual properties
// (the 4th "word", i.e. index = 3, in the string, will be the property name,
// e.g. "public virtual string SomePropertyName"
var virtualPropNames = virtualProps.Select(vp => vp.Trim().Split(' ')).Select(words => words[3]).ToList();
if (virtualPropNames.Count() != virtualProps.Count())
{
throw new Exception("Error: the list of virtual property names does not equal the number of virtual property statements!");
}
// Find all instances of the virtual properties being initialized.
// By examining the overall file for instances of the virtual property name followed by an equal sign,
// we can identify those lines which are statements initializing the virtual property.
var initializeStatements = (from vpName in virtualPropNames
from stmt in inputFileAsListOfStrings
let stmtNoSpaces = stmt.Trim().Replace(" ", "")
where stmtNoSpaces.StartsWith(vpName + "=")
select stmt).ToList();
if (initializeStatements.Count() > 0)
{
Console.WriteLine("Found " + initializeStatements.Count + " initialize statements in the class...");
}
// now process the input based on the found strings and write the output
var outputFileAsListOfStrings = new List<string>();
foreach (var inputLineBeingProcessed in inputFileAsListOfStrings)
{
// is the input line one of the initialize statements identified previously?
// if so, rewrite the line.
// e.g.
// old line: StatusText = statusText;
// becomes: _statusText = statusText;
var isInitStmt = false;
foreach (var initStmt in initializeStatements)
{
if (inputLineBeingProcessed != initStmt) continue;
// we've found our statement; it is an initialize statement;
// now rewrite the format of the line as desired
var prefix = GetCodeLineIndentation(inputLineBeingProcessed);
var tabAndSpaceArray = new[] {' ', '\t'};
var inputLineWithoutPrefix = inputLineBeingProcessed.TrimStart(tabAndSpaceArray);
var outputLine = prefix + ConvertInitialCapitalToUnderscoreAndLowerCase(inputLineWithoutPrefix);
// write the line (preceded by its prefix) to the output file
outputFileAsListOfStrings.Add(outputLine);
Console.WriteLine("Rewrote INPUT: " + initStmt + " to OUTPUT: " + outputLine);
isInitStmt = true;
// we have now processed the input line; no need to loop through the initialize statements any further
break;
}
// if we've already determined the current input line is an initialize statement, no need to proceed further;
// go on to the next input line
if (isInitStmt)
continue;
// is the input line one of the "public virtual SomeType SomePropertyName" statements identified previously?
// if so, rewrite the single line as multiple lines of output.
// the input will look like this:
/*
public virtual SomeType SomePropertyName { get; set; }
*/
// first, we'll need a private variable which corresponds to the original statement in terms of name and type.
// what we'll do is, write the private field AFTER the public property, so as not to interfere with the XML
// comments above the "public virtual" statement.
// the output will be SIX LINES, as follows:
/*
public virtual SomeType SomePropertyName
{
get { return _somePropertyName; }
protected set { _someProperty = value; }
}
private SomeType _somePropertyName;
*/
var isPublicVirtualStatement = false;
foreach (var vp in virtualProps)
{
if (inputLineBeingProcessed != vp) continue;
// the input line being processed is a "public virtual" statement;
// convert it into the six line output format
var thisOutputList = new List<string>();
// first separate any indentation "prefix" that may exist (i.e. tabs and/or spaces),
// from the actual string of text representing the line of code
var prefix = GetCodeLineIndentation(inputLineBeingProcessed);
var tabAndSpaceArray = new[] { ' ', '\t' };
var inputLineWithoutPrefix = inputLineBeingProcessed.TrimStart(tabAndSpaceArray);
var originalVpStmt = inputLineWithoutPrefix.Split(' ');
// first output line (preceded by its original prefix)
var firstOutputLine = prefix +
originalVpStmt[0] + ' ' +
originalVpStmt[1] + ' ' +
originalVpStmt[2] + ' ' +
originalVpStmt[3];
thisOutputList.Add(firstOutputLine);
// second output line (indented to the same level as the original prefix)
thisOutputList.Add(prefix + "{");
// get field name from property name
var fieldName = ConvertInitialCapitalToUnderscoreAndLowerCase(originalVpStmt[3]);
// third output line (indented with the prefix, plus one more tab)
var thirdOutputLine = prefix + "\t" + "get { return " + fieldName + "; }";
thisOutputList.Add(thirdOutputLine);
// fourth output line (indented with the prefix, plus one more tab)
var fourthOutputLine = prefix + "\t" + "protected set { " + fieldName + " = value; }";
thisOutputList.Add(fourthOutputLine);
// fifth output line (indented to the same level as the first curly bracket)
thisOutputList.Add(prefix + "}");
// sixth output line (the "index 2" value of the original statement will be the string representing the .Net type)
// (indentation is the same as the "public virtual" statement above)
var sixthOutputLine = prefix +
"private" + ' ' +
originalVpStmt[2] + ' ' +
fieldName + ";";
thisOutputList.Add(sixthOutputLine);
// now write the six new lines to the master output list
outputFileAsListOfStrings.AddRange(thisOutputList);
isPublicVirtualStatement = true;
Console.WriteLine("Rewrote INPUT: " + inputLineBeingProcessed + " to OUTPUT: <multi-line block>");
break;
}
// if we've already determined the current input line is a "public virtual" statement, no need to proceed further;
// go on to the next input line
if (isPublicVirtualStatement)
continue;
// if we've gotten this far, the input statement is neither an "initialize" statement, nor a "public virtual" statement;
// So just write the output. Don't bother logging this as most lines will not be ones we'll process.
outputFileAsListOfStrings.Add(inputLineBeingProcessed);
}
// write the output file
var newPath = path.Replace(".cs", "-NEW.cs");
File.WriteAllLines(newPath, outputFileAsListOfStrings);
}
}

Console application: How to update the display without flicker?

Using C# 4 in a Windows console application that continually reports progress how can I make the "redraw" of the screen more fluid?
I'd like to do one of the following:
- Have it only "redraw" the part of the screen that's changing (the progress portion) and leave the rest as is.
- "Redraw" the whole screen but not have it flicker.
Currently I re-write all the text (application name, etc.). Like this:
Console.Clear();
WriteTitle();
Console.WriteLine();
Console.WriteLine("Deleting:\t{0} of {1} ({2})".FormatString(count.ToString("N0"), total.ToString("N0"), (count / (decimal)total).ToString("P2")));
Which causes a lot of flickering.
Try Console.SetCursorPosition. More details here: How can I update the current line in a C# Windows Console App?
static void Main(string[] args)
{
Console.SetCursorPosition(0, 0);
Console.Write("################################");
for (int row = 1; row < 10; row++)
{
Console.SetCursorPosition(0, row);
Console.Write("# #");
}
Console.SetCursorPosition(0, 10);
Console.Write("################################");
int data = 1;
System.Diagnostics.Stopwatch clock = new System.Diagnostics.Stopwatch();
clock.Start();
while (true)
{
data++;
Console.SetCursorPosition(1, 2);
Console.Write("Current Value: " + data.ToString());
Console.SetCursorPosition(1, 3);
Console.Write("Running Time: " + clock.Elapsed.TotalSeconds.ToString());
Thread.Sleep(1000);
}
Console.ReadKey();
}
I know this question is a bit old but I found if you set Console.CursorVisible = false then the flickering stops as well.
Here's a simple working demo that shows multi-line usage without flickering. It shows the current time and a random string every second.
private static void StatusUpdate()
{
var whiteSpace = new StringBuilder();
whiteSpace.Append(' ', 10);
var random = new Random();
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var randomWord = new string(Enumerable.Repeat(chars, random.Next(10)).Select(s => s[random.Next(s.Length)]).ToArray());
while (true)
{
Console.SetCursorPosition(0, 0);
var sb = new StringBuilder();
sb.AppendLine($"Program Status:{whiteSpace}");
sb.AppendLine("-------------------------------");
sb.AppendLine($"Last Updated: {DateTime.Now}{whiteSpace}");
sb.AppendLine($"Random Word: {randomWord}{whiteSpace}");
sb.AppendLine("-------------------------------");
Console.Write(sb);
Thread.Sleep(1000);
}
}
The above example assumes your console window is blank to start. If not, make sure to use Console.Clear() first.
Technical Note:
SetCursorPosition(0,0) places the cursor back to the top (0,0) so the next call to Console.Write will start from line 0, char 0. Note, it doesn't delete the previous content before writing. As an example, if you write "asdf" over a previous line such as "0123456", you'll end up with something like "asdf456" on that line. For that reason, we use a whiteSpace variable to ensure any lingering characters from the previous line are overwritten with blank spaces. Adjust the length of the whiteSpace variable to meet your needs. You only need the whiteSpace variable for lines that change.
Personal Note:
For my purposes, I wanted to show the applications current status (once a second) along with a bunch of other status information and I wanted to avoid any annoying flickering that can happen when you use Console.Clear(). In my application, I run my status updates behind a separate thread so it constantly provides updates even though I have numerous other threads and long running tasks going at the same time.
Credits:
Thanks to previous posters and dtb for the random string generator used in the demo.
How can I generate random alphanumeric strings in C#?
You could try to hack something together using the core libraries.
Rather than waste your time for sub-standard results, I would check out this C# port of the ncurses library (which is a library used for formatting console output):
Curses Sharp
I think you can use \r in Windows console to return the beginning of a line.
You could also use SetCursorPosition.
I would recommend the following extension methods. They allow you to use a StringBuilder to refresh the console view without any flicker, and also tidies up any residual characters on each line
The Problem: The following demo demonstrates using a standard StringBuilder, where updating lines that are shorter than the previously written line get jumbled up. It does this by writing a short string, then a long string on a loop:
public static void Main(string[] args)
{
var switchTextLength = false;
while(true)
{
var sb = new StringBuilder();
if (switchTextLength)
sb.AppendLine("Short msg");
else
sb.AppendLine("Longer message");
sb.UpdateConsole();
switchTextLength = !switchTextLength;
Thread.Sleep(500);
}
}
Result:
The Solution: By using the extension method provided below, the issue is resolved
public static void Main(string[] args)
{
var switchTextLength = false;
while(true)
{
var sb = new StringBuilder();
if (switchTextLength)
sb.AppendLineEx("Short msg");
else
sb.AppendLineEx("Longer message");
sb.UpdateConsole();
switchTextLength = !switchTextLength;
Thread.Sleep(500);
}
}
Result:
Extension Methods:
public static class StringBuilderExtensions
{
/// <summary>
/// Allows StrinbBuilder callers to append a line and blank out the remaining characters for the length of the console buffer width
/// </summary>
public static void AppendLineEx(this StringBuilder c, string msg)
{
// Append the actual line
c.Append(msg);
// Add blanking chars for the rest of the buffer
c.Append(' ', Console.BufferWidth - msg.Length - 1);
// Finish the line
c.Append(Environment.NewLine);
}
/// <summary>
/// Combines two StringBuilders using AppendLineEx
/// </summary>
public static void AppendEx(this StringBuilder c, StringBuilder toAdd)
{
foreach (var line in toAdd.ReadLines())
{
c.AppendLineEx(line);
}
}
/// <summary>
/// Hides the console cursor, resets its position and writes out the string builder
/// </summary>
public static void UpdateConsole(this StringBuilder c)
{
// Ensure the cursor is hidden
if (Console.CursorVisible) Console.CursorVisible = false;
// Reset the cursor position to the top of the console and write out the string builder
Console.SetCursorPosition(0, 0);
Console.WriteLine(c);
}
}
I actually had this issue so I made a quick simple method to try and eliminate this.
static void Clear(string text, int x, int y)
{
char[] textChars = text.ToCharArray();
string newText = "";
//Converts the string you just wrote into a blank string
foreach(char c in textChars)
{
text = text.Replace(c, ' ');
}
newText = text;
//Sets the cursor position
Console.SetCursorPosition(x, y);
//Writes the blank string over the old string
Console.WriteLine(newText);
//Resets cursor position
Console.SetCursorPosition(0, 0);
}
It actually worked surprisingly well and I hope it may work for you!
Naive approach but for simple applications is working:
protected string clearBuffer = null; // Clear this if window size changes
protected void ClearConsole()
{
if (clearBuffer == null)
{
var line = "".PadLeft(Console.WindowWidth, ' ');
var lines = new StringBuilder();
for (var i = 0; i < Console.WindowHeight; i++)
{
lines.AppendLine(line);
}
clearBuffer = lines.ToString();
}
Console.SetCursorPosition(0, 0);
Console.Write(clearBuffer);
Console.SetCursorPosition(0, 0);
}
Console.SetCursorPosition(0, 0); //Instead of Console.Clear();
WriteTitle();
Console.WriteLine();
Console.WriteLine("Deleting:\t{0} of {1} ({2})".FormatString(count.ToString("N0")

Resources