How to set scroll position of rich text box control? - winforms

I am using 2 Rich Text Boxes on winforms 4 (customRTB1 and customRTB2).
Both of the rtb's have same text.
What I want to achieve is, when one rtb (customRTB1) is scrolled down, the other rtb (customRTB2) also should be scrolled to exactly same position as customRTB1.
I attempted this:
public class CustomRTB : RichTextBox
{
#region API Stuff
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
[DllImport("user32.dll")]
public static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
private const int SB_HORZ = 0x0;
private const int SB_VERT = 0x1;
#endregion
public int HorizontalPosition
{
get { return GetScrollPos((IntPtr)this.Handle, SB_HORZ); }
set { SetScrollPos((IntPtr)this.Handle, SB_HORZ, value, true); }
}
public int VerticalPosition
{
get { return GetScrollPos((IntPtr)this.Handle, SB_VERT); }
set { SetScrollPos((IntPtr)this.Handle, SB_VERT, value, true); }
}
}
Using HorizontalPosition and VerticalPosition I could move the scroll bar of SECOND rtb as follows:
private void customRTB1_VScroll(object sender, EventArgs e)
{
customRTB2.VerticalPosition = customRTB1.VerticalPosition;
}
This moves the scroll bar of second rtb to the position of first rtb, however, it DOES NOT move the text at all!
So how to make this second rtb to show the corresponding text according to the scroll bar's position?
Mainly, I want every activity happening (editing, scrolling etc)of first rtb to repeat on second rtb.
I know I am very close to the solution.
Please help.

Related

WPF window state when restored from maximized gets stuck in odd state

I'm seeing some strange behaviour from WPF. I have a form with three buttons on it. One button should make the window fullscreen, one should center it on the monitor its currently on, the third button should restore the window to its normal position.
The XAML is
<Window x:Class="TestRestore.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestRestore"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen">
<Grid>
<Button Content="Max" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="94" Click="max_click" Name="max_button"/>
<Button Content="Center" HorizontalAlignment="Left" Margin="10,35,0,0" VerticalAlignment="Top" Width="94" Click="center_click" Name="center_button"/>
<Button Content="Restore" HorizontalAlignment="Left" Margin="227,143,0,0" VerticalAlignment="Top" Width="75" Click="restore_click" Name="restore_button" IsEnabled="False"/>
</Grid>
</Window>
and the code is below. The strange behaviour is that when I maximize, and then restore the window, the position is correctly restored but the window still thinks it's maximized (the maximize button looks like a restore button and you can't resize the window even though ResizeMode has been set to CanResizeWithGrip).
When the maximized window has been restored, and it thinks its still maximized even though the window position isn't maximized, just moving the window manually by dragging the title bar is enough to cause it to correct itself back to non-maximized mode.
Also, if I maximize then restore the window and then maximize it again without moving it, the maximized window position is incorrect (not in the top left).
And the mystery deepens. If I maximize then restore the window, then press alt, then press down (to get the window menu) and select 'Move' and then move the window around with the keyboard, it stays stuck in 'bogus not-mazimized mode' even though the window is being moved, so it seems the only way to unstick it is to move it with the mouse.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace TestRestore
{
public partial class MainWindow : Window
{
WindowStyle old_window_style;
WindowState old_window_state;
double old_left;
double old_top;
double old_width;
double old_height;
public MainWindow()
{
InitializeComponent();
}
// remember position, style and state
private void SaveWindowPos()
{
old_window_style = WindowStyle;
old_window_state = WindowState;
old_left = Left;
old_top = Top;
old_width = Width;
old_height = Height;
max_button.IsEnabled = false;
center_button.IsEnabled = false;
restore_button.IsEnabled = true;
}
// put position, style and state back
private void RestoreWindowPos()
{
WindowStyle = old_window_style;
WindowState = old_window_state;
ResizeMode = ResizeMode.CanResizeWithGrip;
Left = old_left;
Top = old_top;
Width = old_width;
Height = old_height;
max_button.IsEnabled = true;
center_button.IsEnabled = true;
restore_button.IsEnabled = false;
}
// make it centered or fullscreen
private void SetActivePos(bool full_screen)
{
SaveWindowPos();
Hide();
if (full_screen)
{
ResizeMode = ResizeMode.NoResize;
WindowStyle = WindowStyle.None;
WindowState = WindowState.Maximized;
}
else
{
Size s = new Size(800, 600);
Point p = CenterRectInMonitor(this, s);
Left = p.X;
Top = p.Y;
Width = s.Width;
Height = s.Height;
ResizeMode = ResizeMode.NoResize;
WindowState = WindowState.Normal;
}
Show();
}
private void restore_click(object sender, RoutedEventArgs e)
{
Hide();
RestoreWindowPos();
Show();
}
private void max_click(object sender, RoutedEventArgs e)
{
SetActivePos(true);
}
private void center_click(object sender, RoutedEventArgs e)
{
SetActivePos(false);
}
// interop
public const Int32 MONITOR_DEFAULTTOPRIMARY = 0x00000001;
public const Int32 MONITOR_DEFAULTTONEAREST = 0x00000002;
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromWindow(IntPtr handle, Int32 flags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfoEx lpmi);
// size of a device name string
private const int CCHDEVICENAME = 32;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MonitorInfoEx
{
public int Size;
public RectStruct Monitor;
public RectStruct WorkArea;
public uint Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string DeviceName;
public void Init()
{
this.Size = 40 + 2 * CCHDEVICENAME;
this.DeviceName = string.Empty;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RectStruct
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public int Width
{
get
{
return Right - Left;
}
}
public int Height
{
get
{
return Bottom - Top;
}
}
}
public static MonitorInfoEx GetMonitorFromWindow(Window w)
{
var hwnd = new WindowInteropHelper(w).EnsureHandle();
var monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
MonitorInfoEx monitor_info = new MonitorInfoEx();
monitor_info.Init();
GetMonitorInfo(monitor, ref monitor_info);
return monitor_info;
}
// work out how a rect of 'Size size' should be centered on the monitor containing 'Window w'
public static Point CenterRectInMonitor(Window w, Size size)
{
var source = PresentationSource.FromVisual(w);
double x_scale = source.CompositionTarget.TransformToDevice.M11;
double y_scale = source.CompositionTarget.TransformToDevice.M22;
var width = size.Width * x_scale;
var height = size.Height * y_scale;
var monitor_info = GetMonitorFromWindow(w);
Size s = new Size(monitor_info.Monitor.Width, monitor_info.Monitor.Height);
Point p = new Point(monitor_info.Monitor.Left, monitor_info.Monitor.Top);
Point c = new Point(p.X + s.Width / 2, p.Y + s.Height / 2);
return new Point((c.X - width / 2) / x_scale, (c.Y - height / 2) / y_scale);
}
}
}
I don't have a complete answer for you. However you will find that your code starts working a lot better once you remove the Hide() and Show() calls.
private void restore_click(object sender, RoutedEventArgs e)
{
// Hide();
RestoreWindowPos();
// Show();
}
I'm sure you put this in to reduce flicker, but what I think is happening is that the Hide() and Show() calls are flipping the WS_VISIBLE bit in the window style word of the underlying OS window which is the same word which contains the WS_MAXIMIZE and WS_BORDER and some other things that you are manipulating. See https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
It would take more research to figure out what is exactly going on, but the fundamental problem I believe is a "leaky abstraction". Your code sets top, left, style and state as if these were independent uncoupled variables. But they are not! To set left, the OS SetWindowPos() function must be called which requires not the upper left coordinate, the window size, the Z order as well as visibility flags and whether the windows is maximized! See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx. So each time you set one of these "independent" variables you are pounding SetWindowPos(). This API call harks back to the bad old days when CPU cycles were precious and you need to pack as much functionality as possible into each API call.
Ironically this is making your code very inefficient. I think the thing to do to straighten this out is to bypass the leaking abstraction of System.Windows.Window and call SetWindowPos and possibility other API functions directly from user32.dll. Then things will be a lot more predicable.

WPF equivilent of unity 3D's Screen.lockCursor

I haven't used Unity 3D but I gather you can use Screen.lockCursor to take control of the mouse for FPS games. Is this possible in WPF/Win32?
Obviously you have to release it when exiting or in the event of a crash
Thanks
I found the answer spread across a whole bunch of links, so
(1) Set a captureMouse flag, press once to go into this mode, again to come out,
hide the cursor while you are in there
bool captureMouse = false;
private void viewport3D1_MouseDown(object sender, MouseButtonEventArgs e)
{
if (!captureMouse)
{
captureMouse = true;
Mouse.OverrideCursor = Cursors.None;
}
else
{
Mouse.OverrideCursor = null;
captureMouse = false;
}
}
(2) While you're in this mode constantly put the mouse back to the middle of the window
private void theWindow_MouseMove(object sender, MouseEventArgs e)
{
if (!captureMouse)
return;
Point windowPoint = WpfToRealPixels(theWindow, new Point(500, 500));
NativeMethods.SetCursorPos((int)windowPoint.X, (int)windowPoint.Y);
oldP = new Point(500, 500);
}
(3) Translate the co-ords
private Point WpfToRealPixels(Window w, Point p)
{
return theWindow.PointToScreen(p);
}
(4) To put the mouse back you'll need a native Win32 call
public partial class NativeMethods
{
[System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
[return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool SetCursorPos(int X, int Y);
}
Hope that helps someone.

How do I get the current mouse screen coordinates in WPF?

How to get current mouse coordination on the screen?
I know only Mouse.GetPosition() which get mousePosition of element, but I want to get the coordination without using element.
Or in pure WPF use PointToScreen.
Sample helper method:
// Gets the absolute mouse position, relative to screen
Point GetMousePos() => _window.PointToScreen(Mouse.GetPosition(_window));
To follow up on Rachel's answer.
Here's two ways in which you can get Mouse Screen Coordinates in WPF.
1.Using Windows Forms. Add a reference to System.Windows.Forms
public static Point GetMousePositionWindowsForms()
{
var point = Control.MousePosition;
return new Point(point.X, point.Y);
}
2.Using Win32
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
var w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
Do you want coordinates relative to the screen or the application?
If it's within the application just use:
Mouse.GetPosition(Application.Current.MainWindow);
If not, I believe you can add a reference to System.Windows.Forms and use:
System.Windows.Forms.Control.MousePosition;
If you try a lot of these answers out on different resolutions, computers with multiple monitors, etc. you may find that they don't work reliably. This is because you need to use a transform to get the mouse position relative to the current screen, not the entire viewing area which consists of all your monitors. Something like this...(where "this" is a WPF window).
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var mouse = transform.Transform(GetMousePosition());
public System.Windows.Point GetMousePosition()
{
var point = Forms.Control.MousePosition;
return new Point(point.X, point.Y);
}
This works without having to use forms or import any DLLs:
using System.Windows;
using System.Windows.Input;
/// <summary>
/// Gets the current mouse position on screen
/// </summary>
private Point GetMousePosition()
{
// Position of the mouse relative to the window
var position = Mouse.GetPosition(Window);
// Add the window position
return new Point(position.X + Window.Left, position.Y + Window.Top);
}
You may use combination of TimerDispatcher (WPF Timer analog) and Windows "Hooks" to catch cursor position from operational system.
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPos(out POINT pPoint);
Point is a light struct. It contains only X, Y fields.
public MainWindow()
{
InitializeComponent();
DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer();
dt.Tick += new EventHandler(timer_tick);
dt.Interval = new TimeSpan(0,0,0,0, 50);
dt.Start();
}
private void timer_tick(object sender, EventArgs e)
{
POINT pnt;
GetCursorPos(out pnt);
current_x_box.Text = (pnt.X).ToString();
current_y_box.Text = (pnt.Y).ToString();
}
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
This solution is also resolving the problem with too often or too infrequent parameter reading so you can adjust it by yourself. But remember about WPF method overload with one arg which is representing ticks not milliseconds.
TimeSpan(50); //ticks
If you're looking for a 1 liner, this does well.
new Point(Mouse.GetPosition(mWindow).X + mWindow.Left, Mouse.GetPosition(mWindow).Y + mWindow.Top)
The + mWindow.Left and + mWindow.Top makes sure the position is in the right place even when the user drags the window around.
Mouse.GetPosition(mWindow) gives you the mouse position relative to the parameter of your choice.
mWindow.PointToScreen() convert the position to a point relative to the screen.
So mWindow.PointToScreen(Mouse.GetPosition(mWindow)) gives you the mouse position relative to the screen, assuming that mWindow is a window(actually, any class derived from System.Windows.Media.Visual will have this function), if you are using this inside a WPF window class, this should work.
I wanna use this code
Point PointA;
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e) {
PointA = e.MouseDevice.GetPosition(sender as UIElement);
}
private void Button_Click(object sender, RoutedEventArgs e) {
// use PointA Here
}

Some questions about focus on WPF

I've a little problem about focus on WPF.
I whant to create a window, always on top, and that never get the focus (even if we click on it).
Here's my solution :
public partial class SkinWindow : Window
{
public SkinWindow()
{
InitializeComponent();
Loaded += ( object sender, RoutedEventArgs e ) => SetNoActiveWindow();
}
private void SetNoActiveWindow()
{
WindowInteropHelper helper = new WindowInteropHelper( this );
SetWindowLong( helper.Handle, GWL_EXSTYLE, WS_EX_NOACTIVATE );
LockSetForegroundWindow( LSFW_LOCK );
}
const int GWL_EXSTYLE = -20;
const int WS_EX_NOACTIVATE = 134217728;
const int LSFW_LOCK = 1;
[DllImport( "user32" )]
public static extern bool LockSetForegroundWindow( uint UINT );
[DllImport( "user32" )]
public static extern IntPtr SetWindowLong( IntPtr hWnd, int nIndex, int dwNewLong );
}
First problem : It's works, but I've to select an other window to "remove" the focus of my application (after the focus is not gave again, even if I click on my window).
Second problem : When I move or resize the window, the modifications happens when I drop the window.
Do you have any ideas / links / docs ?
Thank you :)
You might want to have a look at this SO post: Make a form not focusable in C#. The answer is specific to Windows Forms. However, the main part is done using Win32 functions, so maybe you can get some ideas from there...

How to remove minimize and maximize buttons from a resizable window?

WPF doesn't provide the ability to have a window that allows resize but doesn't have maximize or minimize buttons. I'd like to able to make such a window so I can have resizable dialog boxes.
I'm aware the solution will mean using pinvoke but I'm not sure what to call and how. A search of pinvoke.net didn't turn up any thing that jumped out at me as what I needed, mainly I'm sure because Windows Forms does provide the CanMinimize and CanMaximize properties on its windows.
Could someone point me towards or provide code (C# preferred) on how to do this?
I've stolen some code I found on the MSDN forums and made an extension method on the Window class, like this:
internal static class WindowExtensions
{
// from winuser.h
private const int GWL_STYLE = -16,
WS_MAXIMIZEBOX = 0x10000,
WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
extern private static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
extern private static int SetWindowLong(IntPtr hwnd, int index, int value);
internal static void HideMinimizeAndMaximizeButtons(this Window window)
{
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
var currentStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX));
}
}
The only other thing to remember is that for some reason this doesn't work from a window's constructor. I got around that by chucking this into the constructor:
this.SourceInitialized += (x, y) =>
{
this.HideMinimizeAndMaximizeButtons();
};
One way is to set your ResizeMode="NoResize". It will behave like this.
Don't know if this works for your req. visually.. This is
<Window x:Class="DataBinding.MyWindow" ...Title="MyWindow" Height="300" Width="300"
WindowStyle="ToolWindow" ResizeMode="CanResizeWithGrip">
If anyone use Devexpress window (DXWindow) accepted answer doesn't work. One ugly approach is
public partial class MyAwesomeWindow : DXWindow
{
public MyAwesomeWIndow()
{
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
// hides maximize button
Button button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_Maximize.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
// hides minimize button
button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_Minimize.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
// hides close button
button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_CloseButton.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
}
}
Here's a solution I'm using. Note that maximize button is still displayed.
Markup:
<Window x:Class="Example"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Example"
StateChanged="Window_StateChanged">
Code behind:
// Disable maximizing this window
private void Window_StateChanged(object sender, EventArgs e)
{
if (this.WindowState == WindowState.Maximized)
this.WindowState = WindowState.Normal;
}
This variant of the solution proposed by #MattHamilton can (and must) be called in the constructor of the Window. The trick is to subscribe a delegate to the SourceInitialized event within the extension method.
private const int GWL_STYLE = -16, WS_MAXIMIZEBOX = 0x10000, WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
extern private static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
extern private static int SetWindowLong(IntPtr hwnd, int index, int value);
/// <summary>
/// Hides the Minimize and Maximize buttons in a Window. Must be called in the constructor.
/// </summary>
/// <param name="window">The Window whose Minimize/Maximize buttons will be hidden.</param>
public static void HideMinimizeAndMaximizeButtons(this Window window)
{
window.SourceInitialized += (s, e) => {
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
int currentStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
};
}
You can set the ResizeMode="NoResize" of the window if you want to remove Minimize and Maximize button
Just use
WindowStyle="ToolWindow"
It hides the maximize and minimize buttons, but the window can still be resized by dragging the window borders and minimize using the hide button in the bottom right corner of the taskbar.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.windowstyle?view=windowsdesktop-6.0

Resources