I have a simple TextBox:
<TextBox Name="PART_txtBx" IsReadOnly="True" ScrollViewer.VerticalScrollBarVisibility="Auto" />
and from codebehind i add some text every while:
public MainWindow()
{
InitializeComponent();
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(100);
dt.Tick += dt_Tick;
dt.Start();
}
void dt_Tick(object sender, EventArgs e)
{
PART_txtBx.Text += "hi\n";
}
when i click on the textbox it automatically put the scrollbar on top when i add some text to it.
if I handle the PreviewMouseLeftButtonDown event like this:
private void PART_txtBx_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
the textbox works normally but i can't (of course) select any text.
any ideas to prevent this behavior?
EDIT 1:
I noticed when the textbox is created there isn't any caret in it even if it has got focus. the caret is shown only when the click is executed but i can't find what's changed inside the textbox after the click.
My TextBox is read only so i don't need the caret.
May: PART_txtBx.CaretIndex =PART_txtBx.Length;
or:
PART_txtBx.SelectionStart = PART_txtBx.Text.Length;
PART_txtBx.ScrollToCaret();
ScrollToEnd().
with textbox_changed event
You need to extend your TextBox control to add PreviewTextChanged event, like it is described here. On PreviewTextChanged you will have to remember SelectionStart and SelectionLength
Handle event ScrollViewer.ScrollChanged for your TextBox:
ScrollViewer.ScrollChanged="OnScrollChanged" <!--in xaml-->
private void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
// here you could prevent scrolling
isOnBottom = (sender as TextBox).VerticalOffset + (sender as TextBox).ViewportHeight == (sender as TextBox).ExtentHeight;
}
Handle TextChanged event:
TextChanged="OnTextChanged"
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (isOnBottom) (sender as TextBox).ScrollToEnd();
// and here you can select text that was selected earlier
// by using remembered SelectionStart and SelectionLength
}
In the case of manual text entering you will have to prevent text selection
Hope, it helps
So i created my own CustomTextblock for resolving this issue:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CE.EthernetMessagesManager.Views
{
public class CustomTextBox : TextBox
{
private int realCaretIndex = 0;
private bool triggeredByUser = false;
private bool isScrollingWithMouse = false;
public CustomTextBox()
: base()
{
this.PreviewMouseLeftButtonDown += CustomTextBox_PreviewMouseLeftButtonDown;
this.PreviewMouseLeftButtonUp += CustomTextBox_PreviewMouseLeftButtonUp;
this.PreviewMouseMove += CustomTextBox_PreviewMouseMove;
this.PreviewMouseWheel += CustomTextBox_PreviewMouseWheel;
this.TextChanged += CustomTextBox_TextChanged;
this.LostFocus += CustomTextBox_LostFocus;
this.AddHandler(ScrollViewer.ScrollChangedEvent, new RoutedEventHandler((X, S) =>
{
if ((S as ScrollChangedEventArgs).VerticalChange != 0 && triggeredByUser)
{
TextBox textBox = (X as TextBox);
int newLinePosition = 0;
newLinePosition = (int)(((S as ScrollChangedEventArgs).VerticalChange + textBox.ExtentHeight) / (textBox.FontSize + 2));
realCaretIndex += newLinePosition * ((S as ScrollChangedEventArgs).VerticalChange < 0 ? -1 : 1);
if (realCaretIndex < 0)
realCaretIndex = 0;
textBox.CaretIndex = realCaretIndex;
triggeredByUser = false;
}
}));
}
void CustomTextBox_LostFocus(object sender, System.Windows.RoutedEventArgs e)
{
e.Handled = true;
}
void CustomTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
textBox.CaretIndex = realCaretIndex;
var max = (textBox.ExtentHeight - textBox.ViewportHeight);
var offset = textBox.VerticalOffset;
if (max != 0 && max == offset)
this.Dispatcher.Invoke(new Action(() =>
{
textBox.ScrollToEnd();
}),
System.Windows.Threading.DispatcherPriority.Loaded);
}
void CustomTextBox_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
triggeredByUser = true;
}
void CustomTextBox_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (isScrollingWithMouse)
{
TextBox textBox = sender as TextBox;
realCaretIndex = textBox.GetCharacterIndexFromPoint(Mouse.GetPosition(textBox), true);
}
}
void CustomTextBox_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
isScrollingWithMouse = false;
}
void CustomTextBox_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
TextBox textBox = sender as TextBox;
realCaretIndex = textBox.GetCharacterIndexFromPoint(Mouse.GetPosition(textBox), true);
triggeredByUser = true;
isScrollingWithMouse = true;
}
}
}
this CustomTextBox also pin the scrollbar to the bottom when i manually put it to bottom.
the xaml:
<v:CustomTextBox
IsReadOnly="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
/>
Sadly with this implementation selection is broken. I'll look into it
Related
I am facing an issue over here. I want to remove the dynamic created button by press the X button.
The function will be user press X button, then press the button he/she want to delete, the button will be remove.
For now my program is able to create a new button but I do not know how to delete it, here is my coding, please help me , thanks very much!!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class graphtest : Form
{
public graphtest()
{
InitializeComponent();
}
private Point Origin_Cursor;
private Point Origin_Control;
private bool BtnDragging = false;
private void button1_Click(object sender, EventArgs e)
{
var b = new Button();
b.Width = 54;
b.Height = 58;
b.Image = Image.FromFile(#"C:\Users\prod01\Desktop\Mote.png");
b.Text = "";
b.Name = "button";
//b.Click += new EventHandler(b_Click);
b.MouseUp += (s, e2) => { this.BtnDragging = false; };
b.MouseDown += new MouseEventHandler(this.b_MouseDown);
b.MouseMove += new MouseEventHandler(this.b_MouseMove);
this.Controls.Add(b);
}
private void b_MouseDown(object sender, MouseEventArgs e)
{
Button ct = sender as Button;
ct.Capture = true;
this.Origin_Cursor = System.Windows.Forms.Cursor.Position;
this.Origin_Control = ct.Location;
this.BtnDragging = true;
}
private void b_MouseMove(object sender, MouseEventArgs e)
{
if (this.BtnDragging)
{
Button ct = sender as Button;
ct.Left = this.Origin_Control.X - (this.Origin_Cursor.X - Cursor.Position.X);
ct.Top = this.Origin_Control.Y - (this.Origin_Cursor.Y - Cursor.Position.Y);
}
}
private void graphtest_Load(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
for (int ix = this.Controls.Count - 1; ix >= 0; ix--)
{
if (this.Controls[ix] is Button) this.Controls[ix].Dispose();
}
}
}
}
Here is how you can do that
//a list where you save all the buttons created
List<Button> buttonsAdded = new List<Button>();
private void button2_Click(object sender, EventArgs e)
{
Button myText = new Button();
myText.Tag = counter;
myText.Location = new Point(x2,y2);
myText.Text = Convert.ToString(textBox3.Text);
this.Controls.Add(myText);
//add reference of the button to the list
buttonsAdded.Insert(0, myText);
}
//atach this to a button removing the other buttons
private void removingButton_Click(object sender, EventArgs e)
{
if (buttonsAdded.Count > 0)
{
Button buttonToRemove = buttonsAdded[0];
buttonsAdded.Remove(buttonToRemove);
this.Controls.Remove(buttonToRemove);
}
}
Ref :- how to delete a button run time?
i am making a windows form application in which i used a datagridview.
i want that when i write something in textbox in datagridview,than a messagebox appears containing the string i wrote..
ican't get my text in textchanged event..
all thing must be fired in textchanged event..
here is my code:-
void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView1.CurrentCell.ColumnIndex == 1)
{
TextBox tb = (TextBox)e.Control;
tb.TextChanged += new EventHandler(tb_TextChanged);
}
}
void tb_TextChanged(object sender, EventArgs e)
{
//listBox1.Visible = true;
//string firstChar = "";
//this.listBox1.Items.Clear();
//if (dataGridView1.CurrentCell.ColumnIndex == 1)
{
string str = dataGridView1.CurrentRow.Cells["Column2"].Value.ToString();
if (str != "")
{
MessageBox.Show(str);
}
}
void tb_TextChanged(object sender, EventArgs e)
{
var enteredText = (sender as TextBox).Text
...
}
Showing MessageBox in TextChanged will be very annoying.
Instead you could try it in DataGridView.CellValidated event which is fired after validation of the cell is completed.
Sample code:
dataGridView1.CellValidated += new DataGridViewCellEventHandler(dataGridView1_CellValidated);
void dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)
{
if (dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value != null)
{
MessageBox.Show(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString());
}
}
This may be a basic question but I just started using WPF and I am having troubles trying to do a simple drag and drop.
I created this ToolboxButton class:
public class ToolboxButton : Button
{
private bool _isDragging = false;
private Point _startPoint;
public ToolboxButton(string content)
{
Content = content;
HorizontalAlignment = HorizontalAlignment.Stretch;
Height = 30;
Loaded += ToolboxButton_Loaded;
}
void ToolboxButton_Loaded(object sender, RoutedEventArgs e)
{
PreviewMouseLeftButtonDown += ToolboxButton_PreviewMouseLeftButtonDown;
PreviewMouseMove += ToolboxButton_PreviewMouseMove;
}
void ToolboxButton_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && !_isDragging)
{
Point position = e.GetPosition(null);
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
StartDrag(e);
}
}
}
void ToolboxButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_startPoint = e.GetPosition(null);
}
private void StartDrag(MouseEventArgs e)
{
_isDragging = true;
DataObject data = new DataObject(System.Windows.DataFormats.Text.ToString(), "abcd");
DragDrop.DoDragDrop(e.Source as ToolboxButton, data, DragDropEffects.Move);
_isDragging = false;
}
}
This button is added in a stackpanel like so:
ToolboxButton btnAddButton = new ToolboxButton("Button");
_toolboxView.Children.Add(btnAddButton); // _toolboxView is a stackpanel
And I have a Canvas with the following code:
public class DesignerView : Canvas
{
public DesignerView()
{
AllowDrop = true;
DragOver += DesignerView_DragOver;
Drop += DesignerView_Drop;
PreviewDragOver += DesignerView_PreviewDragOver;
}
void DesignerView_PreviewDragOver(object sender, DragEventArgs e)
{
MessageBox.Show("previewdragover");
}
void DesignerView_DragOver(object sender, DragEventArgs e)
{
MessageBox.Show("dragover");
if (!e.Data.GetDataPresent(typeof(ToolboxButton)))
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
}
void DesignerView_Drop(object sender, DragEventArgs e)
{
MessageBox.Show("drop");
if (e.Data.GetDataPresent(typeof(ToolboxButton)))
{
ToolboxButton droppedThingie = e.Data.GetData(typeof(ToolboxButton)) as ToolboxButton;
MessageBox.Show("You dropped: " + droppedThingie.Content);
}
}
public UIElement GetView()
{
return this;
}
}
Both Canvas and StackPanel are added in the main window like so:
Grid contentGrid = new Grid();
Content = contentGrid;
contentGrid.Children.Add(_toolboxView.GetView());
contentGrid.Children.Add(_designerView.GetView());
None of the MessageBoxes ever fire and I can't find out why. The cursor changes to the "Cannot pin", a dark circle with a diagonal line inside.
Am I missing something ? I want everything to be done in the code without XML.
Maybe I have to do something on the StackPanel but I tried the code of ToolboxButton there and it didn't work either.
As I can see you done all job, just DesignerView_drop left to correct.
use sender object to grab dragged object (in this example button)
void DesignerView_Drop(object sender, DragEventArgs e)
{
MessageBox.Show("drop");
Button btn = (Button)sender;
contentGrid.Children.Add(btn);
}
By clear i mean to redraw or paint or color the control back to it's original.
This is the working code:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
namespace FTP_ProgressBar
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
txtHost.TextChanged += anyTextBox_TextChanged;
txtUploadFile.TextChanged += anyTextBox_TextChanged;
txtDir.TextChanged += anyTextBox_TextChanged;
anyTextBox_TextChanged(null, null);
if ((txtHost.Text == "") || txtUploadFile.Text == "")
{
btnUpload.Enabled = false;
}
if (txtDir.Text == "")
{
checkBox1.Enabled = false;
}
}
private void anyTextBox_TextChanged(object sender, EventArgs e)
{
btnUpload.Enabled = txtHost.TextLength > 0 && txtUploadFile.TextLength > 0;
checkBox1.Enabled = txtDir.TextLength > 0;
this.Invalidate();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnBrowse_Click(object sender, EventArgs e)
{
if(this.openFileDialog1.ShowDialog() != DialogResult.Cancel)
this.txtUploadFile.Text = this.openFileDialog1.FileName;
}
private void btnUpload_Click(object sender, EventArgs e)
{
if(this.ftpProgress1.IsBusy)
{
this.ftpProgress1.CancelAsync();
this.btnUpload.Text = "Upload";
}
else
{
FtpSettings f = new FtpSettings();
f.Host = this.txtHost.Text;
f.Username = this.txtUsername.Text;
f.Password = this.txtPassword.Text;
f.TargetFolder = this.txtDir.Text;
f.SourceFile = this.txtUploadFile.Text;
f.Passive = this.chkPassive.Checked;
try
{
f.Port = Int32.Parse(this.txtPort.Text);
}
catch { }
this.toolStripProgressBar1.Visible = true;
this.ftpProgress1.RunWorkerAsync(f);
this.btnUpload.Text = "Cancel";
}
}
private void ftpProgress1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.toolStripStatusLabel1.Text = e.UserState.ToString(); // the message will be something like: 45 Kb / 102.12 Mb
this.toolStripProgressBar1.Value = Math.Min(this.toolStripProgressBar1.Maximum, e.ProgressPercentage);
}
private void ftpProgress1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
MessageBox.Show(e.Error.ToString(), "FTP error");
else if(e.Cancelled)
this.toolStripStatusLabel1.Text = "Upload Cancelled";
else
this.toolStripStatusLabel1.Text = "Upload Complete";
this.btnUpload.Text = "Upload";
this.toolStripProgressBar1.Visible = false;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Pen penBorder;
if (txtHost.TextLength <= 0)
{
penBorder = new Pen(Color.Red, 3);
e.Graphics.DrawRectangle(penBorder, txtHost.Location.X, txtHost.Location.Y, txtHost.Width - 1, txtHost.Height - 1);
}
if (txtUploadFile.TextLength <= 0)
{
penBorder = new Pen(Color.Red, 3);
e.Graphics.DrawRectangle(penBorder, txtUploadFile.Location.X, txtUploadFile.Location.Y, txtUploadFile.Width - 1, txtUploadFile.Height - 1);
}
}
}
}
I saw now that without a breakpoint if i minimize form1 when the program is running after typed text in both textBoxes and then resize the form1 it does clear the rectangles.
Strange it seems that it's taking effect only when i minimize and resize back the form1.
In the TextChanged event i tried to add: txtHost.Invalidate(); but it didn't help.
The only way that the rectangle get clear is if i minmize and resize back form1.
Or adding this.Invalidate(); did the trick.
OnPaint() only gets called when the window needs to be updated. This is a basic principle about how Windows works. If you need the window to be updated now then, yes, you need to invalidate the window so that OnPaint() will be called.
But is it ok to redraw all the form?
Sure, but it's not very performant as you are redrawing areas that don't necessarily need redrawing. Invalidate() should have a version that accepts a rectangle argument. Use it to only invalidate the area you want to update.
Imagine that i have 10 buttons and i want to use mouse enter event to increase buttons width and height by 30. if i want to do this i have to use mouse event for each of them one by one,
how can i write my code only once for all buttons.i'm trying to use foreach loop but not sure if i should use form load and don't know how to handle event. (windows form)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace main
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (Control item in Controls)
{
if (item is Button)
{
// code
}
}
}
}
}
First of all shorten your code by getting only the buttons on the form. Then add an eventhandler for the MouseEnter event:
foreach (Button btn in Controls.OfType<Button>())
{
btn.MouseEnter += new System.EventHandler(btn_MouseEnter);
}
Then in the event change the width:
private void btn_MouseEnter(object sender, System.EventArgs e)
{
var senderButton = (Button)sender;
senderButton.Width += 30;
}
You'all also have to add an event handler + event for MouseLeave to decrease the width again. Otherwise the buttons will keep on growing:
foreach (Button btn in Controls.OfType<Button>())
{
btn.MouseEnter += new System.EventHandler(btn_MouseEnter);
btn.MouseLeave += new System.EventHandler(btn_MouseLeave );
}
private void btn_MouseEnter(object sender, System.EventArgs e)
{
var senderButton = (Button)sender;
senderButton.Width += 30;
}
private void btn_MouseLeave (object sender, System.EventArgs e)
{
var senderButton = (Button)sender;
senderButton.Width -= 30;
}
The foreach-loop code can be added after InitializeComponent() or in the Form_Load event.
private void Form1_Load(object sender, System.EventArgs e)
{
foreach (Button btn in Controls.OfType<Button>())
{
btn.MouseEnter += new System.EventHandler(btn_MouseEnter);
btn.MouseLeave += new System.EventHandler(btn_MouseLeave );
}
}
You can easily loop through your all buttons using OfType extension method like this:
foreach(var button in this.Controls.OfType<Button>())
{
button.MouseEnter += button_MouseEnter;
}
Use functions to wrap your logic.
Write your code that change the weight and width in method, an subscribe to MouseMove or whatever event that suits you.
Then use GetChildAtPoint to locate the button, so you can change its size.
// This goes in the
foreach(var button in this.Controls.OfType<Button>())
{
button.Mouse += button_IncreaseSize;
}
protected override void button_IncreaseSize(MouseEventArgs e)
{
// use GetChildAtPoint to get the control
var button = GetChildAsPoint(new Point(e.X, e.Y));
// Change the size of button, eg. with Scal, and with the Size property, or by chaning Height and Width propertis manually
button.Scale(new SizeF(30, 30));
}
Alternatively you can also do this:
private void Form1_Load(object sender, EventArgs e)
{
foreach (Control item in this.Controls)
{
if (item is Button)
{
//item.MouseEnter += (s, ea) => { YourMethodName(); };
item.MouseEnter += new System.EventHandler(btn_MouseEnter);
}
}
}
private void btn_MouseEnter(object sender, System.EventArgs e)
{
//your code to increase width and height
var btnSenderButton = sender as Button;
btnSenderButton.Width += 30;
}