I have a listboxcontrol where I'm inserting Log text
and display always the last lines
listBoxControl1.SelectedIndex = listBoxControl1.Items.Count - 1;
but when the user scolls up/down the list, I wish to maintain the user's scroll position
,
a condition to cancel the code above
I tried with MouseUp / MouseDown but these event won't fire on clicking the ScrollBar
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBoxControl1.Items.Add(e.UserState);
if (!mouseDown) // this is not working !!
listBoxControl1.SelectedIndex = listBoxControl1.Items.Count - 1;
}
Thanks
I believe you can use the following approach:
SubscribeScrollEvent(listBoxControl1); // Before start items adding
bw.RunWorkerAsync();
//...
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
UnsubscribeScrollEvent(listBoxControl1); // After items adding complete
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) {
listBoxControl1.Items.Add(e.ProgressPercentage);
if(!userScrollPerformed)
listBoxControl1.SelectedIndex = listBoxControl1.Items.Count - 1;
}
//...
void SubscribeScrollEvent(ListBoxControl listBox) {
var hScroll = listBox.Controls[0] as DevExpress.XtraEditors.HScrollBar;
var vScroll = listBox.Controls[1] as DevExpress.XtraEditors.VScrollBar;
vScroll.Scroll += ListBox_Scroll;
hScroll.Scroll += ListBox_Scroll;
}
void UnubscribeScrollEvent(ListBoxControl listBox) {
var hScroll = listBox.Controls[0] as DevExpress.XtraEditors.HScrollBar;
var vScroll = listBox.Controls[1] as DevExpress.XtraEditors.VScrollBar;
vScroll.Scroll -= ListBox_Scroll;
hScroll.Scroll -= ListBox_Scroll;
}
bool userScrollPerformed;
void ListBox_Scroll(object sender, ScrollEventArgs e) {
if(e.Type == ScrollEventType.ThumbTrack)
userScrollPerformed = true; // set a flag
}
Related
Example:
I have a Panel or flowLayoutpanel so I want to scroll look into items without using scrollbar, instead I want to use MouseWheel
So I was wondering is there any way that you can scroll up or down in a panel or flowLayoutPanel
Without use 'Auto Scroll= true' instead I want to moveViewPanel up or down when mouse scrolled.
If you get what I mean
These are some code that I used with but seemed not to work at all
--Sameple1
void Panel2_mWheel(object sender, MouseEventArgs e)
{
//Get cursor position
Point mousePoint = new Point(e.X, e.Y);
//Change to the location of the form of this form
mousePoint.Offset(this.Location.X, this.Location.Y);
if (panel2.RectangleToScreen(panel2.DisplayRectangle).Contains(mousePoint))
{
Console.WriteLine("Contain");
panel2.AutoScrollPosition = new Point(0, panel2.VerticalScroll.Value - e.Delta);
}
}
--Sample2
Panel.MouseEnter += C_MouseEnter;
void Panel_MouseEnter(object sender, EventArgs e)
{
c.Focus();
}
--sample3
panel.MouseWheel += pn_MouseWheel;
private void pn_MouseWheel(object sender, MouseEventArgs e)
{
int deltaScroll = 10;
if (e.Delta > 0)
{
if (pnlContain.VerticalScroll.Value - deltaScroll >= pnlContain.VerticalScroll.Minimum)
pnlContain.VerticalScroll.Value -= deltaScroll;
else
pnlContain.VerticalScroll.Value = pnlContain.VerticalScroll.Minimum;
}
else
{
if (pnlContain.VerticalScroll.Value + deltaScroll <= pnlContain.VerticalScroll.Maximum)
pnlContain.VerticalScroll.Value += deltaScroll;
else
pnlContain.VerticalScroll.Value = pnlContain.VerticalScroll.Maximum;
}
--sample 4
panel_wheel(object sender, MouseEventArgs e)
{
if(e.OldValue>e.NewValue)
{
//Scroll up Do stuff
}
else
{
//Scroll down Do stuff
}
}
Anyone help me please!!
Is there any way to detect if ListView or CollectionView is scrolling by user and not from ScrollTo method?
I am using ScrollTo as the example below:
colViewCategories.ScrollTo(categoryItem, null, ScrollToPosition.Center, true);
Or if i can disable Scrolled Event till ScrollTo Method will stop to scroll.
I finally found my answer.
bool scrollAnimationIsRaised=false;
int previousindex = 0;
private System.Timers.Timer tmr = new System.Timers.Timer();
private void InitTimer
{
tmr.Interval = 500; //waiting 500ms after scrollTo has Stop for not having conflicts
tmr.Elapsed += Tmr_Elapsed;
}
private void ButtonClick(object sender, EventArgs e)
{
scrollAnimationIsRaised = true;
listview.ScrollTo(50);
}
private void ListviewItems_Scrolled(object sender, ItemsViewScrolledEventArgs e)
{
if(scrollAnimationIsRaised)
{
//ScrollToEvent Is Fired
if (e.LastVisibleItemIndex == previousindex)
tmr.Start();
previousindex = e.LastVisibleItemIndex;
}
else
{
//ScrollTo event has finished
}
}
private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
tmr.Stop();
scrollAnimationIsRaised = false;
}
I am pasting an item from a TreeView to a TextBox, but I want to paste that item in the mouse's current position and also show a caret like the image below.
Image with caret:
Here is my code:
private void tvOperador_ItemDrag(object sender, ItemDragEventArgs e)
{
var node = (TreeNode)e.Item;
if (node.Level > 0)
{
DoDragDrop(node.Text, DragDropEffects.Copy);
}
}
private void txtExpresion_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(string))) e.Effect = DragDropEffects.Copy;
}
private void txtExpresion_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
string Item = (System.String)e.Data.GetData(typeof(System.String));
string[] split = Item.Split(':');
txtExpresion.Text += split[1];
}
}
This is tricky as the Drag&Drop operation keeps the mouse captured, so you can't use the mouse events..
One way is to set up a Timer to do the work..:
Timer cursTimer = new Timer();
void cursTimer_Tick(object sender, EventArgs e)
{
int cp = txtExpresion.GetCharIndexFromPosition(
txtExpresion.PointToClient(Control.MousePosition));
txtExpresion.SelectionStart = cp;
txtExpresion.SelectionLength = 0;
txtExpresion.Refresh();
}
The Timer uses the Control.MousePosition function to determined the cursor position every 25ms or so, sets the caret and updates the TextBox.
In your events you initialize it and make sure the TextBox has focus; finally you add the string at the current selection:
private void txtExpresion_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(string)))
{
e.Effect = DragDropEffects.Copy;
txtExpresion.Focus();
cursTimer = new Timer();
cursTimer.Interval = 25;
cursTimer.Tick += cursTimer_Tick;
cursTimer.Start();
}
}
private void txtExpresion_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
cursTimer.Stop();
string Item = (System.String)e.Data.GetData(typeof(System.String));
string[] split = Item.Split(':');
txtExpresion.SelectedText = split[1]
}
}
A different way to solve it would be to not use normal Drag&Drop and only code the mouse events but this one worked ok a my first tests.
Update
While the above solution does work, using a Timer seems not exactly elegant. Much better to use the DragOver event, as seen in Reza's answer. But instead of painting a cursor, why not do the real thing, i.e. take control of the actual I-beam..?
The DragOver event is called all the time during the move so it works pretty much like MousMove would: So here is a merger of the two solutions, which I believe is the best way to do it:
private void txtExpresion_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
string Item = (System.String)e.Data.GetData(typeof(System.String));
string[] split = Item.Split(':');
txtExpresion.SelectionLength = 0;
txtExpresion.SelectedText = split[1];
}
}
private void txtExpresion_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(string)))
{
e.Effect = DragDropEffects.Copy;
txtExpresion.Focus();
}
}
private void txtExpresion_DragOver(object sender, DragEventArgs e)
{
int cp = txtExpresion.GetCharIndexFromPosition(
txtExpresion.PointToClient(Control.MousePosition));
txtExpresion.SelectionStart = cp;
txtExpresion.Refresh();
}
You can draw a caret over TextBox in DragOver event. Also set the SelectionStart to the char index you get from mouse position. Then in DragDrop event, just set SelectedText.
private void textBox1_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
var position = textBox1.PointToClient(Cursor.Position);
var index = textBox1.GetCharIndexFromPosition(position);
textBox1.SelectionStart = index;
textBox1.SelectionLength = 0;
textBox1.Refresh();
using (var g = textBox1.CreateGraphics())
{
var p = textBox1.GetPositionFromCharIndex(index);
g.DrawLine(Pens.Black, p.X, 0, p.X, textBox1.Height);
}
}
}
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(System.String)))
{
string txt = (System.String)e.Data.GetData(typeof(System.String));
textBox1.SelectedText = txt;
}
}
My code that works:
private void textBox1_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = true;
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = false;
}
If I add something for the computer to do, as seen in the following code example, the computer does not show the progress bar until it's done doing the computation. What I want it to do is show the progress bar first, then do the computation, then on some other event I want to hide the progress bar. Why can't I do it this way?
private void textBox1_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = true;
FindPrimeNumber(50000);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = false;
}
The requested FindPrimeNumber code:
public int FindPrimeNumber(int n)
{
int count = 0;
int a = 2;
while (count < n)
{
int b = 2;
int prime = 1;// to check if found a prime
while (b * b <= a)
{
if (a % b == 0)
{
prime = 0;
break;
}
b++;
}
if (prime > 0)
count++;
a++;
}
return (--a);
}
the FindPrimeNumber code is just something to make the computer do a task for a while, so I can test to see if my progress bar is going to show.
I figured it out. In this example, a user enters a 5 digit number in a text box, then a progress bar shows up on the form as it is processing the number in a math function, then the result is put into a second text box and the progress bar goes away.
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.TextLength == 5)
{
progressBar1.Visible = true;
int textFromTextBox1 = Int32.Parse(textBox1.Text);
backgroundWorker1.RunWorkerAsync(textFromTextBox1);
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = FindPrimeNumber((int)e.Argument);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox2.Text = e.Result.ToString();
backgroundWorker1.CancelAsync();
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = false;
}
Note: This example does not handle all exceptions, but it works great. As you can probably see in the code I am passing a value into the BackgroundWorker which gets passed into the FindPrimeNumber method, then I am retrieving the result out of the BackgroundWorker.
More notes for newbies:
I have the BackgroundWorker property WorkerSupportsCancellation set to True.
In WinForms, after dropping the BackgroundWorker onto the form, when you double click it, it will generate the DoWorkEventHandler for you, then in the Solution Explorer go to the Events and double click on RunWorkerCompleted so it can generated that for you as well. Otherwise, you will have to do a lot of manual code entry.
I have a WPF form which runs a background operation with progress bar. but the problem is that;
when the operation is completed, the progress bar is still running. I mean it shows like the operation is in progress.
how can I stop that? here is my whole code;
System.ComponentModel.BackgroundWorker mWorker;
private void button1_Click(object sender, RoutedEventArgs e) {
mWorker = new System.ComponentModel.BackgroundWorker();
mWorker.DoWork +=new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
mWorker.ProgressChanged +=new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
mWorker.WorkerReportsProgress = true;
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
while (pbProcessing.Value != 100) {
if (!mWorker.CancellationPending) {
try {
pbProcessing.Value = (pbProcessing.Value + 0.01) % 100;
} catch (System.Exception ex) {
// No action required
}
} else {
MessageBox.Show(this, "Process cancelled", "Cancel Process", MessageBoxButton.OK);
break;
}
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
new System.Threading.ThreadStart(delegate { }));
}
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
// Do your work here, its on seperate thread
System.Threading.Thread.Sleep(10000);
}
private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
pbProcessing.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
// Stop Progressbar updatation
Window1 w = new Window1();
w.Browser.Navigate(new Uri("http://stackoverflow.com"));
w.Show();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (mWorker != null) {
if (mWorker.IsBusy) {
mWorker.CancelAsync();
}
}
}
If you want to hide the progressbar after the work is done, set its Visibility property to Visibility.Hidden. If you just want to reset it to its initial state, set it's Value to 0 (or to pbProgressing.Minimum, if you changed that from its default value).
As a side note, your code doesn't really make sense: Instead of continuously changing pbProcessing.Value in the button event handler (which is completely useless, since no UI updates are performed until the button event handler has completed), you should only change the value in ProgressChanged. I.e., your code should look something like this:
System.ComponentModel.BackgroundWorker mWorker;
private void button1_Click(object sender, RoutedEventArgs e) {
mWorker = new System.ComponentModel.BackgroundWorker();
mWorker.DoWork +=new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
mWorker.ProgressChanged +=new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
mWorker.WorkerReportsProgress = true;
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
// Don't do anything else here
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
for (int i = 1; i < 100; i++) {
mWorker.ReportProgress(i);
// Do some part of the work
System.Threading.Thread.Sleep(100);
// Check if the user wants to abort
if (mWorker.CancellationPending) {
e.Cancel = true;
return;
}
}
mWorker.ReportProgress(100); // Done
}
private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
pbProcessing.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
// Stop Progressbar updatation
Window1 w = new Window1();
w.Browser.Navigate(new Uri("http://stackoverflow.com"));
w.Show();
// Check the result
if (e.Cancelled) {
// show the message box that the task has been canceled
}
// Reset Progress bar
pbProcessing.Value = 0;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (mWorker != null) {
if (mWorker.IsBusy) {
mWorker.CancelAsync();
}
}
}