I have a panel whose AutoScroll = true;
I can scroll the panel using scrollbars.
I also find mousewheel "vertical scroll" with "mouse" wheel by writing:
void panelInner_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
panelInner.Focus();
}
However, I want to scroll horizontally by "wheeling mouse + shift" too.
What do I need to do for that to happen?
In your designer file, you'll need to manually add a MouseWheel event delegate.
this.panelInner.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panelInner_MouseWheel);
Then, in your code behind, you can add the following.
private const int WM_SCROLL = 276; // Horizontal scroll
private const int SB_LINELEFT = 0; // Scrolls one cell left
private const int SB_LINERIGHT = 1; // Scrolls one line right
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
private void panelInner_MouseWheel(object sender, MouseEventArgs e)
{
if (ModifierKeys == Keys.Shift)
{
var direction = e.Delta > 0 ? SB_LINELEFT : SB_LINERIGHT;
SendMessage(this.panelInner.Handle, WM_SCROLL, (IntPtr)direction, IntPtr.Zero);
}
}
References:
Shift + mouse wheel horizontal scrolling
Mouse tilt wheel horizontal scrolling in C#
Related
I'm facing the following issue:
In WPF I have a window with WindowStyle="None" and so I add a button to move the window with the DragMove() method. This part is working fine. What I want in addition is that when the window reach a certain position it's stop the DragMove.
My idea was to make it by raising a MouseLeftButtonLeft thinking it will interrupt the DragMove but it isn't.
Button to move window:
<Button Grid.Column="0" x:Name="MoveButton" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="3" Cursor="Hand">
<Image x:Name="MoveImage" Source="images/move.png" MouseLeftButtonDown="MoveWindow" MouseLeftButtonUp="Poney" />
</Button>
Method to move the window:
// Move the window with drag of the button. Ensure that we are not over the taskbar
private void MoveWindow(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
DragMove();
// https://stackoverflow.com/questions/48399180/wpf-current-screen-dimensions
System.Drawing.Rectangle workingArea = Screen.FromHandle(new System.Windows.Interop.WindowInteropHelper(this).Handle).WorkingArea;
//The left property of the window is calculated on the width of all screens, so use VirtualScreenWidth to have the correct width
if (Top > (workingArea.Height - Height))
{
Top = (workingArea.Height - Height);
}
else if (Left > (SystemParameters.VirtualScreenWidth - Width))
{
Left = (SystemParameters.VirtualScreenWidth - Width);
}
else if (Left < 0)
{
Left = 0;
}
}
Method to raise event :
private void MainWindow_LocationChanged(object sender, EventArgs e)
{
// https://stackoverflow.com/questions/48399180/wpf-current-screen-dimensions
System.Drawing.Rectangle workingArea = Screen.FromHandle(new System.Windows.Interop.WindowInteropHelper(this).Handle).WorkingArea;
if(Left > 2000)
{
MouseButtonEventArgs mouseButtonEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left)
{
RoutedEvent = MouseLeftButtonUpEvent,
Source = MoveImage
};
MoveImage.RaiseEvent(mouseButtonEvent);
//InputManager.Current.ProcessInput(mouseButtonEvent);
}
}
Check that the event is raised :
public void Poney(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("Poney");
}
I have the 'poney' displaying in my console, so i guess the code to raise the event is working?
In short version I need a method to interrupt the dragmove so I can make some change and the restart DragMove.
Thanks :)
PS : The 2000 value is used for test, in "real" the position is calculated.
You could use the native mouse_event function to synthesize a mouse up event, e.g.:
const int MOUSEEVENTF_LEFTUP = 0x04;
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
void MainWindow_LocationChanged(object sender, EventArgs e)
{
if (Left > 1000)
{
Point point = Mouse.GetPosition(this);
mouse_event(MOUSEEVENTF_LEFTUP, (uint)point.X, (uint)point.Y, 0, 0);
}
}
I am building a WPF 4.5 Application that has controls that enable the User to "Lock" and "Unlock" the Application's Height.
In order to lock the Height, I am following this StackOverflow answer regarding setting the MinHeight and MaxHeight to the same value.
In order to unlock the Height, I set MinHeight=0 and MaxHeight=double.PositiveInfinity
This all appears to be working fine.
The problem I'm encountering that I haven't been able to solve is that when the height is "Locked", when I mouseover the right edge of the Application Window, the cursor turns into the horizontal resize cursor.
Is there a way I can disable that so that the cursor stays as the regular pointer in WPF?
I am on WPF 4.5.
I saw this post that has answers showing how to do it in Win32: WPF: Make window unresizeable, but keep the frame?.
This post is over 3 years old, and I was just wondering (hoping) maybe WPF has evolved since then.
Thank you very much in advance!
Philip
On your startup Window (MainWindow.xaml), try making a binding for the Window's ResizeMode property and then modifying it to 'NoResize' when you don't want it to be resizable. To make it resizable, change it to 'CanResize'.
Hope that helps!
You need to set MinWidth = MaxWidth = Width = your desired width as mentioned in this StackOverflow answer regarding setting the MinHeight and MaxHeight to the same value.
In addition you need to hook the winproc for your window and process the WM_NCHITTEST message.
#region Vertical Resize Only
// ReSharper disable InconsistentNaming
private const int WM_NCHITTEST = 0x0084;
private const int HTBORDER = 18;
private const int HTBOTTOM = 15;
private const int HTBOTTOMLEFT = 16;
private const int HTBOTTOMRIGHT = 17;
private const int HTLEFT = 10;
private const int HTRIGHT = 11;
private const int HTTOP = 12;
private const int HTTOPLEFT = 13;
private const int HTTOPRIGHT = 14;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr DefWindowProc(
IntPtr hWnd,
int msg,
IntPtr wParam,
IntPtr lParam);
// ReSharper restore InconsistentNaming
#endregion Vertical Resize Only
public CanConfigurationDialog()
{
InitializeComponent();
Loaded += MainWindowLoaded;
}
#region Vertical Resize Only
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
try
{
// Obtain the window handle for WPF application
var mainWindowPtr = new WindowInteropHelper(this).Handle;
var mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc?.AddHook(WndProc);
}
catch (Exception)
{
;
}
}
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Override the window hit test
// and if the cursor is over a resize border,
// return a standard border result instead.
if (msg == WM_NCHITTEST)
{
handled = true;
var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
switch (htLocation)
{
case HTTOP:
case HTTOPLEFT:
case HTTOPRIGHT:
htLocation = HTTOP;
break;
case HTBOTTOM:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
htLocation = HTBOTTOM;
break;
case HTLEFT:
case HTRIGHT:
htLocation = HTBORDER;
break;
}
return new IntPtr(htLocation);
}
return IntPtr.Zero;
}
#endregion Vertical Resize Only
This will prevent the horizontal resize cursor from being displayed!
Q.E.D.
I have a multi-line TextBox with a vertical scrollbar that logs data from real-time processing. Currently, whenever a new line is added by textBox.AppendText(), the TextBox scrolls to the bottom so you can see the last entry, this great. But I have a checkbox to indicate whether TextBox is allowed to auto-scroll. Is there any way to do this?
Note:
I want to use the TextBox because the added text has multi-lines and alignment by whitespace, so it's not simple to use with a ListBox or a ListView.
I tried to add a new line by textBox.Text += text, but the TextBox constantly scrolls to the top.
If we have a solution to do that, then one more question is how to prevent the TextBox auto scrolls when the user uses the scrollbar to view somewhere else in the TextBox while the TextBox appends text?
private void OnTextLog(string text)
{
if (chkAutoScroll.Checked)
{
// This always auto scrolls to the bottom.
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
// This always auto scrolls to the top.
//txtLog.Text += Environment.NewLine + text;
}
else
{
// I want to append the text without scrolls right here.
}
}
Update 1: As saggio suggests, I also think the solution to this problem is to determine the position of the first character in the current text that is displayed in the TextBox before appending text and restoring it after that. But how to do this? I tried to record the current cursor position like this, but it did not help:
int selpoint = txtLog.SelectionStart;
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
txtLog.SelectionStart = selpoint;
Update 2 (the issue was resolved): I found a solution that can solve my issue here on Stack Overflow. I have optimized their code to suit my case as follows:
// Constants for extern calls to various scrollbar functions
private const int SB_VERT = 0x1;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 0x4;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
{
int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
textbox.AppendText(text + Environment.NewLine);
if (autoscroll)
{
int VSmin, VSmax;
GetScrollRange(textbox.Handle, SB_VERT, out VSmin, out VSmax);
int sbOffset = (int)((textbox.ClientSize.Height - SystemInformation.HorizontalScrollBarHeight) / (textbox.Font.Height));
savedVpos = VSmax - sbOffset;
}
SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
private void OnTextLog(string text)
{
AppendTextToTextBox(txtLog.Text, Environment.NewLine + text, chkAutoScroll.Checked);
}
Another way:
private const int SB_VERT = 0x1;
private const int WM_VSCROLL = 0x115;
private const int SB_THUMBPOSITION = 0x4;
private const int SB_BOTTOM = 0x7;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);
private void AppendTextToTextBox(TextBox textbox, string text, bool autoscroll)
{
int savedVpos = GetScrollPos(textbox.Handle, SB_VERT);
textbox.AppendText(text + Environment.NewLine);
if (autoscroll)
{
PostMessageA(textbox.Handle, WM_VSCROLL, SB_BOTTOM, 0);
}
else
{
SetScrollPos(textbox.Handle, SB_VERT, savedVpos, true);
PostMessageA(textbox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * savedVpos, 0);
}
}
I post these solutions for those who have a similar issue. Thanks for cgyDeveloper's source code.
Does anyone have a more straightforward way?
This seems pretty straight forward but I may be missing something. Use append text to scroll to the position if Autochecked is true and just add the text if you do not wish to scroll.
Update...I was missing something. You want to set the selection point and then scroll to the caret. See below.
if (chkAutoScroll.Checked)
{
// This always auto scrolls to the bottom.
txtLog.AppendText(Environment.NewLine);
txtLog.AppendText(text);
// This always auto scrolls to the top.
//txtLog.Text += Environment.NewLine + text;
}
else
{
int caretPos = txtLog.Text.Length;
txtLog.Text += Environment.NewLine + text;
txtLog.Select(caretPos, 0);
txtLog.ScrollToLine(txtLog.GetLineIndexFromCharacterIndex(caretPos));
}
You have to do it something like this,
textBox1.AppendText("Your text here");
// this selects the index zero as the location of your caret
textBox1.Select(0, 0);
// Scrolls to the caret :)
textBox1.ScrollToCaret();
Tested and working on VS2010 c# Winforms, i dont know about WPF but google probably has the answer for you.
The desired actions are:
To turn on autoscrolling when the scrollbar is dragged to the bottom.
To turn off autoscrolling when the scollbar is dragged anywhere else.
create the following class
public class AutoScrollTextBox : TextBox
{
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
bool isScrolledToEnd = VerticalOffset + ViewportHeight == ExtentHeight;
base.OnTextChanged(e);
CaretIndex = Text.Length;
if (isScrolledToEnd)
{
ScrollToEnd();
}
}
}
And replace the TextBox with AutoScrollTextBox in your XML and append to the TextToDisplay binding as stuff arrives for display
<local:AutoScrollTextBox Text="{Binding TextToDisplay }" />
I have a ListView in which i want to create an event when the VScrollBar appears. I actully dont want a horizontal scrollbar and whenever the VScrollbar appears i want to resize the columns so that it fits the window. I already can check for the visiblity of a scrollbar but i dont know the name of the event which is triggered when the ScrollBars appear.
Here is my code :
private const int WS_VSCROLL = 0x200000;
private const int GWL_STYLE = -16;
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int Index);
private static bool IsScrollbarVisible(IntPtr hWnd)
{
bool bVisible = false;
int nMessage = WS_VSCROLL;
int nStyle = GetWindowLong(hWnd, GWL_STYLE);
bVisible = ((nStyle & nMessage) != 0);
return bVisible;
}
And Works Like this :
if (IsScrollbarVisible(listview.Handle))
{
columnHeader1.Width = listview.ClientRectangle.Width - (columnHeader2.Width + columnHeader3.Width);
}
Someone Please Help Me!
ClientSizeChanged Event will fire but to get it work correct we have to add BeginUpdate() and EndUpdate()..
This Code does everything :
private void listview_ClientSizeChanged(object sender, EventArgs e)
{
listview.BeginUpdate();
if (IsScrollbarVisible(listview.Handle))
{
columnHeader1.Width = listview.ClientRectangle.Width - (columnHeader2.Width + columnHeader3.Width);
}
listview.EndUpdate();
}
I have a treeview with a fixed size. How can i make it scrollable to horizontal and vertical? When i set Scrollable to true, it adds only vertical scroll and when scroll reach the bottom of the form and there is text remaining the scroll didn't go down.Help me please.
p.s sorry for poor english
You can try this approach
private const int WM_SCROLL = 276; // Horizontal scroll
private const int WM_VSCROLL = 277; // Vertical scroll
private const int SB_LINEUP = 0; // Scrolls one line up
private const int SB_LINELEFT = 0;// Scrolls one cell left
private const int SB_LINEDOWN = 1; // Scrolls one line down
private const int SB_LINERIGHT = 1;// Scrolls one cell right
private const int SB_PAGEUP = 2; // Scrolls one page up
private const int SB_PAGELEFT = 2;// Scrolls one page left
private const int SB_PAGEDOWN = 3; // Scrolls one page down
private const int SB_PAGERIGTH = 3; // Scrolls one page right
private const int SB_PAGETOP = 6; // Scrolls to the upper left
private const int SB_LEFT = 6; // Scrolls to the left
private const int SB_PAGEBOTTOM = 7; // Scrolls to the upper right
private const int SB_RIGHT = 7; // Scrolls to the right
private const int SB_ENDSCROLL = 8; // Ends scroll
[DllImport("user32.dll",CharSet=CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg,IntPtr wParam, IntPtr lParam);
SendMessage(treeView.Handle, WM Scroll Message, (IntPtr) Scroll Command ,IntPtr.Zero);
Scroll page up
SendMessage(treeView.Handle, WM_VSCROLL,(IntPtr)SB_PAGEUP,IntPtr.Zero);
Scroll page down
SendMessage(treeView.Handle, WM_VSCROLL,(IntPtr)SB_PAGEDOWN,IntPtr.Zero);
What ended up working for me was fixing up [keyboardP]'s code snippet:
myTreeView.Nodes[myTreeView.Nodes.Count - 1].EnsureVisible();
presto. horizontal bar.