im trying to get a textbox to autoscroll a line down every X seconds.
i have found the AutoScrollOffset and ScrollToCaret functions, but these functions do not give the desired result.
I think my solution would be to do the autoscroll function in a backgroundworkerthread, that does a scroll down by 1 line every x seconds. But i have no idea how to, and info from the net isnt verry usefull either.
I hope someone can help me, thnx in advance!
(im using .net 4.5)
Borrowing heavily from this answer, and combining it with a Timer, you could do something like this:
private int lineNumber = 1;
private void timer1_Tick(object sender, EventArgs e)
{
nfobox.HideSelection = false;
nfobox.SelectionStart = nfobox.GetFirstCharIndexFromLine(lineNumber - 1);
nfobox.SelectionLength = nfobox.Lines[lineNumber - 1].Length;
nfobox.ScrollToCaret();
lineNumber++;
// include some code to detect the last line, or you'll get an exception
}
This will only work if your lines are separated by a line feed.
If it's one long line that wraps, then the whole thing will be selected on the first "tick" and then it's done.
"Grant Winney" is rigth. you can't directly modify the Uİ from a background thread.
But You use the way below.
int lineCounter = 0;
int nextLineLength = 0;
private void timer1_Tick(object sender, EventArgs e)
{
textBox1.SelectionStart = nextLineLength;
textBox1.SelectionLength = 0;
textBox1.ScrollToCaret();
nextLineLength += textBox1.Lines[lineCounter++].Length + "\r\n".Length; //"\r\n" is next line parameters
}
Related
I have been trying to finish this small application for counting the number of attempts if you fail to write the correct pin code.
This is my code so far, and I am not understanding why my code isn't working and it keeps on incrementing non stop.
private void btn_login_Click(object sender, EventArgs e)
{
int attempts = 0;
int pin_number;
do
{
pin_number = int.Parse(txt_pin.Text);
MessageBox.Show("Hi There");
if (pin_number != 1326)
{
attempts++;
MessageBox.Show($"pin code is incorrect you have {attempts} of 3 attempts left");
txt_pin.Text = "";
txt_pin.Focus();
}
} while (pin_number == 1326 && attempts < 3);
}
Every time you click the button is an "attempt", correct? Well, what's the first thing you do every time you click the button...
int attempts = 0;
So every attempt is the first attempt. Except when the user gets it right. Then what you have is an infinite loop because the pin_number is correct and attempts is never incremented.
First, get rid of the loop entirely. There's no need to repeatedly check the same input. Once it's checked, it's checked. Second, track the number of attempts outside the scope of each attempt, such as at the class level. Third, check the number of attempts. Perhaps something like this:
private int attempts = 0;
private void btn_login_Click(object sender, EventArgs e)
{
int pin_number;
pin_number = int.Parse(txt_pin.Text);
MessageBox.Show("Hi There");
if (attempts < 3 && pin_number != 1326)
{
attempts++;
MessageBox.Show($"pin code is incorrect you have {attempts} of 3 attempts left");
txt_pin.Text = "";
txt_pin.Focus();
}
}
Now it's at least checking the pin as expected. Though at this point you have some logic to reconsider for your program. Off the top of my head...
It never notifies the user if they got the pin right. Perhaps something else should happen?
After the number of attempts is exhausted, there's no warning to indicate this. It looks exactly as it does if the pin is correct.
The text implies that the number of attempts is counting down, but it's actually counting up.
Perhaps something like this might get you started:
private int attempts = 3;
private void btn_login_Click(object sender, EventArgs e)
{
int pin_number;
pin_number = int.Parse(txt_pin.Text);
MessageBox.Show("Hi There");
if (attempts <= 0)
{
MessageBox.Show($"No more attempts left");
}
else if (pin_number != 1326)
{
attempts--;
MessageBox.Show($"Pin code is incorrect you have {attempts} attempts left");
txt_pin.Text = "";
txt_pin.Focus();
}
}
Examine each statement in the logic. For your own logic, particularly around if blocks and loops and whatnot, perhaps even grab a piece of paper and draw out the different code paths and write down in each path what should happen there. Every detail is important, such as when to show a message or when to modify a value. There's a lot of polish that can be added to this code, and I imagine it's an academic exercise so I'll leave that to you.
Right now you have a loop inside your button click handler. You probably don't want that since you want to enable the user to enter a new pin code and click the Login button again.
But that also means that you need to store the number of attempts outside the click handler, since it needs to be saved from one click to the next.
So if you change your code to something like this, I think you'll get the functionality you're after
private int attempts = 0;
private void btn_login_Click(object sender, EventArgs e)
{
int pin_number;
pin_number = int.Parse(txt_pin.Text);
MessageBox.Show("Hi There");
if (pin_number != 1326)
{
attempts++;
MessageBox.Show($"pin code is incorrect you have {3-attempts} of 3 attempts left");
txt_pin.Text = "";
txt_pin.Focus();
}
if (attempts >= 3)
{
btn_login.Enabled = false;
}
}
I feel stupid to ask this question but i got headache trying to find out why this simple for loop didn't work.
private void button2_Click(object sender, EventArgs e)
{
for (int i = 1; i>=5; i++)
label2.Text = "aaaa";
}
you're using a greater than sign, use the less than
private void button2_Click(object sender, EventArgs e)
{
for (int i = 1; i<=5; i++)
label2.Text = "aaaa";
}
first iteration: 1==5 which evaluates to false, so it will exit
You should read how the for loop works.
Statement 1 is executed before the loop (the code block) starts. Statement 2 defines the condition for running the loop (the code block), if it is true Statement 3 is executed after the loop (the code block) has been executed.
And this repeats until statement 2 becomes false
I'm trying to get the contents of a StringBuilder to display in a WinForms textbox, but the window never appears when I try to compile the program. This is my first venture into WinForms and C# and I've only used the language for about a week and a half now, so this is probably a simple fix that I'm just not seeing.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
Random rand = new Random();
int[] builderList = new int[10000];
for (int i = 0; i < 10000; i++)
{
builderList[i] = rand.Next(1, 20000);
builder.Append(builderList[i].ToString() + " ");
}
// This is the line that seems to be the problem...
textBox1.Text = builder.ToString();
}
}
When I try to run the program and insert a breakpoint on that last line of code, I can see that the program seems to just hit that line continuously. Oddly enough, if I change that line to this:
textBox1.Text = "Hey, lol";
my program will run. I checked the debugger in Visual Studio and saw that the contents of 'builderList' are updated to random numbers and 'builder' looks like it's correctly storing the values in 'builderList' as a string like I want, so I'm kind of confused about what's going on here. I'd appreciate any help I can get on this one as it seems like it should be a relatively easy fix but I've been stumped on it so far and I haven't really found anything helpful in the MSDN documentation.
Thanks so much!
Change your TextBox1 to be a MultiLine TextBox.Select "Allow multiLine"
The real reason for this is about the pixel width, check my post and #TaW's answer here: The maximum number of characters a TextBox can display
So far I was putting all code that was reaction to event directly into event handling method.
Yesterday I saw somebody somewhere mentioning that only minimum of code should go there.
Is that true ? Or whats the best practice ?
e.g. which one of the examples is better from program-smooth-working point of view, and why, if you may:
Fig1:
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
var DropPosX = e.X;
string[] s = (string[])e.Data.GetData(DataFormats.FileDrop, false);
for (int i = 0; i < s.Length; i++)
{
CheckFile(s[i])
LoadFile(s[i]);
// ..big chunk of code..
}
// ..big chunk of code..
}
Fig2:
DoDragDrop(int[] s, int DropPosX)
{
for (int i = 0; i < s.Length; i++)
{
CheckFile(s[i])
LoadFile(s[i]);
// ..big chunk of code..
}
// ..big chunk of code..
}
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
var DropPosX = e.X;
string[] s = (string[])e.Data.GetData(DataFormats.FileDrop, false);
DoDragDrop(s, DropPos);
}
..or even
Fig3:
int DropPosX;
string[] s;
DoDragDrop()
{
for (int i = 0; i < s.Length; i++)
{
CheckFile(s[i])
LoadFile(s[i]);
// ...
}
// ...
}
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
DropPosX = e.X;
s = (string[])e.Data.GetData(DataFormats.FileDrop, false);
DoDragDrop();
}
Most event handlers essentially take the following two actions
Gather relevant data from the event, possible just that the event happened
Perform an action using the gathered data
It sounds like that person was suggesting that you break up these logical operations into 2 separate methods. This is sound reasoning but it is also an issue of style. It doesn't make your program more or less correct to do this. Although I generally find that code is more testable if you take this approach.
Specific to this sample though. I would not use instance variables in this case. The items being dragged and the position are relevant to the DoDragDrop method and should be passed as arguments. If that data needs to be persisted then DoDragDrop should be the one to set the instance values.
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
int position = e.X;
string[] items = (string[])e.Data.GetData(DataFormats.FileDrop, false);
DoDragDrop(positon, items);
}
Yes you should try to keep a minimum in there.
It can get a bit messy if big chunk of code is say twiddling about with a load of internals (e.g. form controls, but you aim for as much as you can outside of the eventhandler.
All depends on what the big chunk of code is, but even a local private method is better than a huge lump of code in and eventHandler.
If say you were grabbing UI properties to store them in another class. Add a method to it, that takes them as arguments.
If it's a lot of UI stuff, look at a UserControl.
The main reason to do it, is testing UI is a major pain, so the less logic in the UI, the easier the job.
I'm only just getting into GUIs, so please forgive me if I've missed something obvious. Google hasn't helped much I'm afraid.
My base objective is to have Marquee style text scrolling across the screen a set number of times. I've achieved the scrolling aspect utilizing a timer which scrolls a label named "My text here" across the screen (taken from this tutorial: http://www.youtube.com/watch?v=-y-Z0i-DeAs Note: I don't speak the language he does), but I've been unable to get the thing to stop. I'm open to using a different way to achieve the scrolling, but this is the only example I've found so far that worked well with my current level of knowledge of GUIs (basically dragging and dropping).
private void timer_Tick(object sender, EventArgs e)
{
this.Refresh();
labelTesting.Left += 5;
if (labelTesting.Left >= this.Width)
{
labelTesting.Left = labelTesting.Width * -1;
}
}
My best guess is that the timer is simply starting the whole process over with each tick. I've tried countering this by having it return after running i times and telling the label what to display, but that's not working. Nor can I seem to find a way to tell the thing to stop.
http://www.dotnetperls.com/timer has an example where a timer is set to run for a given amount of time, but I do not know how to implement that when messing around with GUIs. What would be the best way to implement the feature I desire? Any insight or suggestions would be greatly appreciated.
EDIT: Based on suggestions in the answers and comments I have edited the code to run for what should be 30 seconds before setting itself to a given position. However the text no longer scrolls. I'm going to keep working at it, but more input would be appreciated.
private void timer_Tick(object sender, EventArgs e)
{
var time = DateTime.Now;
if(time < DateTime.Now.AddSeconds(-30)) // you decide when to stop scrolling
{
Timer timer = (Timer)sender;
timer.Stop();
labelTesting.Left = 0; // or wherever it should be at the end of the scrolling
}
this.Refresh();
labelTesting.Left += 5;
if (labelTesting.Left >= this.Width)
{
labelTesting.Left = labelTesting.Width * -1;
}
}
You need to stop the timer:
private void timer_Tick(object sender, EventArgs e)
{
if (someConditionToEndScroll) // you decide when to stop scrolling
{
Timer timer = (Timer) sender;
timer.Stop();
labelTesting.Left = 0; // or wherever it should be at the end of the scrolling
}
this.Refresh();
labelTesting.Left += 5;
if (labelTesting.Left >= this.Width)
{
labelTesting.Left = labelTesting.Width * -1;
}
}