When I edit the treenode (with long name), it has a background color:
How can I remove the background? How can I change the background color?
I was trying to remove it in OnDrawNode, but it is impossible to do.
Next, I tried to use the TVM_GETEDITCONTROL message and send WM_CTLCOLOREDIT, but it doesn't work too:
[DllImport("user32.dll")]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
internal const int WM_CTLCOLOREDIT = 0x0133;
internal const int TVM_GETEDITCONTROL = 0x110F;
private void NodeTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
TreeNode nodeEditing = e.Node;
IntPtr editControlHandle = SendMessage(NodeTree.Handle, (uint)TVM_GETEDITCONTROL, IntPtr.Zero, IntPtr.Zero);
if (editControlHandle != IntPtr.Zero)
{
SendMessage(editControlHandle, (uint)WM_CTLCOLOREDIT, ColorTranslator.ToWin32(Color.Red), new IntPtr(1));
}
}
This background - the background of the selected node.
In the event BeforeLabelEdit I unsubscribe from AfterSelect, memorize the selected node and deselect nodes:
private TreeNode SelectedNodeBeforeEdit;
private void NodeTree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
NodeTree.AfterSelect -= NodeTree_AfterSelect;
SelectedNodeBeforeEdit = NodeTree.SelectedNode;
NodeTree.SelectedNode = null;
}
In the event AfterLabelEdit I subscribe to AfterSelect and exhibit the selected node:
private void NodeTree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
NodeTree.SelectedNode = SelectedNodeBeforeEdit;
NodeTree.AfterSelect += NodeTree_AfterSelect;
}
Related
I’m trying to disable popped contextmenu of ThemedWindow of devexpress when I click titlebar using mouserightbutton.
I used below code and it works in 'Window', but 'ThemedWindow' isn't working:
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
HwndSource hwndSource = HwndSource.FromHwnd(windowHandle);
hwndSource.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0xa4)
{
handled = true;
}
return IntPtr.Zero;
}
If I want to do same thing in ThemedWindow, what should I do?
I found the solution in below url:
https://supportcenter.devexpress.com/ticket/details/t728500/themedwindow-doesn-t-have-the-allowsystemmenu-property
I want to drag a PictureBox, and I have managed to do so. But my application doesn't do it as smoothly as Windows photo viewer. I mean the difference isn't huge or anything, but it's noticeable. Is there something I could do to make it a little less choppy? This is my simple code:
int MOUSE_X = 0;
int MOUSE_Y = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
picBox.Image = Image.FromFile(#"D:\test_big.png");
picBox.Width = 3300;
picBox.Height = 5100;
}
private void picBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
MOUSE_X = e.X;
MOUSE_Y = e.Y;
}
}
private void picBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
picBox.Left = picBox.Left + (e.X - MOUSE_X);
picBox.Top = picBox.Top + (e.Y - MOUSE_Y);
}
}
Here's a demo that illustrates your approach and the suggested one in the comments.
Testing your code produces:
Whereas the suggested code:
using System.Runtime.InteropServices;
//...
private const int WM_SYSCOMMAND = 0x112;
private const int MOUSE_MOVE = 0xF012;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(
IntPtr hWnd,
int wMsg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll")]
private static extern int ReleaseCapture(IntPtr hWnd);
private void picBox_MouseMove(object sender, MouseEventArgs e)
{
if (!DesignMode && e.Button == MouseButtons.Left)
{
ReleaseCapture(picBox.Handle);
SendMessage(picBox.Handle, WM_SYSCOMMAND, (IntPtr)MOUSE_MOVE, IntPtr.Zero);
}
}
Produces:
Note that, I also use a background image to make the situation worse if I may say that. However, without the background image, it hard to detect which code snippet is used.
I am trying to adapt the code posted in this question:
https://stackoverflow.com/a/44059700
to allow me to embed a Unity3D app inside a WPF app.
This is my slightly edited version:
namespace WPFWithUnity
{
public partial class Page1 : Page
{
[DllImport("User32.dll")]
static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private Process process;
private IntPtr unityHWND = IntPtr.Zero;
private const int WM_ACTIVATE = 0x0006;
private readonly IntPtr WA_ACTIVE = new IntPtr(1);
private readonly IntPtr WA_INACTIVE = new IntPtr(0);
Frame p = MainWindow.Instance.floatingFrame;
bool initialized = false;
public Page1()
{
InitializeComponent();
MainWindow.Instance.MainWindowClosing += Application_Exit;
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += attemptInit;
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
dispatcherTimer.Start();
}
void attemptInit(object sender, EventArgs e) {
if (initialized)
return;
HwndSource source = (HwndSource)HwndSource.FromVisual(p);
Console.WriteLine("attempting to get handle...");
if (source == null) {
Console.WriteLine("Failed to get handle source");
return;
}
IntPtr hWnd = source.Handle;
try
{
process = new Process();
process.StartInfo.FileName = "Child.exe";
process.StartInfo.Arguments = "-parentHWND " + hWnd.ToInt32() + " " + Environment.CommandLine;
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForInputIdle();
// Doesn't work for some reason ?!
//unityHWND = process.MainWindowHandle;
EnumChildWindows(hWnd, WindowEnum, IntPtr.Zero);
//unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
Console.WriteLine("Unity HWND: 0x" + unityHWND.ToString("X8"));
panel1_Resize(this, EventArgs.Empty);
initialized = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to UnityGame.exe.");
}
}
private void ActivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
}
private void DeactivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
}
private int WindowEnum(IntPtr hwnd, IntPtr lparam)
{
unityHWND = hwnd;
ActivateUnityWindow();
return 0;
}
private void panel1_Resize(object sender, EventArgs e)
{
MoveWindow(unityHWND, 0, 0, (int)p.Width, (int)p.Height, true);
Console.WriteLine("RESIZED UNITY WINDOW TO: " + (int)p.Width + "x" + (int)p.Height);
ActivateUnityWindow();
}
// Close Unity application
private void Application_Exit(object sender, EventArgs e)
{
try
{
process.CloseMainWindow();
Thread.Sleep(1000);
while (!process.HasExited)
process.Kill();
}
catch (Exception)
{
}
}
private void Form1_Activated(object sender, EventArgs e)
{
ActivateUnityWindow();
}
private void Form1_Deactivate(object sender, EventArgs e)
{
DeactivateUnityWindow();
}
}
}
And here is the relevant part of the XAML:
<Frame Name="floatingFrame" Grid.Row="15" Grid.RowSpan="5" Grid.Column="0" Grid.ColumnSpan="2" Width="640" Height="480" Margin="100,0,0,0" Panel.ZIndex="100" Source="Page1.xaml"/>
Really, the only difference is that I'm trying to use a WPF Page inside a Frame instead of a WinForms panel (trying to avoid WinForms). The embedded Unity app starts up fine...except that it takes up the whole window (i.e. you can't see any of the WPF controls anymore).
So, the question:
How do I get the Unity app to only stay inside the WPF page (which is inside a Frame)?
enter image description here
(The Y of this XY problem would be that I'm just trying to create a 3D graphics display of something inside a WPF app.)
Thanks in advance for any help.
Use a WindowsFormsHost or HwndHost control in your WPF. The hwnd is in the host control's Handle property. So you can change this line to put Unity in just the host control.
process.StartInfo.Arguments = "-parentHWND " + hwndHost.Handle.ToInt32() + " " + Environment.CommandLine;
And remove the code that gets the hwnd for the floating frame
HwndSource source = (HwndSource)HwndSource.FromVisual(p);
The problem with the above solution is that getting focus on the Unity-exe seems to be not possible. So yes, I was able to load the exe on a certain cell of a certain user control of a certain WPF application, but could not click on anything in the Unity-frame.
I spent the whole day searching for a solution and can now come with a solution, that is in my opinion cleaner and also solves the problem with the focus.
I describe what you can do in steps:
Documentation of Unity : In here, it is explained how you can embed a Unity-exe in a Winforms control. There is even a .zip-file "EmbeddedWindow.zip" where you can download example code. Copy the essential files out of the Container .
Form1.cs contains exactly the code as in this question.
public partial class Form1: Form
{
[DllImport("User32.dll")]
private static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
private Process process;
private IntPtr unityHWND = IntPtr.Zero;
private const int WM_ACTIVATE = 0x0006;
private readonly IntPtr WA_ACTIVE = new IntPtr(1);
private readonly IntPtr WA_INACTIVE = new IntPtr(0);
public Form1()
{
InitializeComponent();
TopLevel = false;
try
{
process = new Process();
process.StartInfo.FileName = "[INSERT_FILE_NAME_OF_YOUR_EXE].exe";
process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;
process.StartInfo.UseShellExecute = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForInputIdle();
// Doesn't work for some reason ?!
//unityHWND = process.MainWindowHandle;
EnumChildWindows(panel1.Handle, WindowEnum, IntPtr.Zero);
unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + ".\nCheck if Container.exe is placed next to Child.exe.");
}
}
private void ActivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
}
private void DeactivateUnityWindow()
{
SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
}
private int WindowEnum(IntPtr hwnd, IntPtr lparam)
{
unityHWND = hwnd;
ActivateUnityWindow();
return 0;
}
private void panel1_Resize(object sender, EventArgs e)
{
MoveWindow(unityHWND, 0, 0, panel1.Width, panel1.Height, true);
ActivateUnityWindow();
}
// Close Unity application
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
process.CloseMainWindow();
Thread.Sleep(1000);
while (process.HasExited == false)
process.Kill();
}
catch (Exception)
{
}
}
private void Form1_Activated(object sender, EventArgs e)
{
ActivateUnityWindow();
}
private void Form1_Deactivate(object sender, EventArgs e)
{
DeactivateUnityWindow();
}
}
Do not forget to have a look in "Form1.cs", it is possible that the exe you want to update is not "Child.exe", so if it is another one, just edit the string in the code. process.StartInfo.FileName = "[INSERT_FILE_NAME_OF_YOUR_EXE].exe"; Also check "SelectablePanel.cs", where the setting of Selectable to true is essential. This SelectablePanel is used in the Form1.Designer.cs .
The left panel1 of the SplitContainer is a SelectablePanel instead of a Panel !
class SelectablePanel : Panel
{
public SelectablePanel()
{
this.SetStyle(ControlStyles.Selectable, true);
this.TabStop = true;
}
}
Please also note that in the constructor of the Winforms-control, I needed to set Toplevel = false; . This was not mentioned in the example of Unity but is needed to avoid an exception when you embed it in your WPF application.
Go to your WPF Application and make a User Control that will contain your Winforms-Control. Make a control similar to what is done in this link . In this example, you have a Grid named Grid_To_Embed_Winforms_Control_In and a small piece of code-behind, like the underlying code.
public partial class WPF_User_Control: UserControl
{
public bool Already_Loaded = false;
public WPF_User_Control()
{
InitializeComponent();
}
private void On_Load(object sender, RoutedEventArgs e)
{
if (!Already_Loaded)
{
// Create the interop host control.
var host =
new WindowsFormsHost();
// Embed the Winforms Control
host.Child = new Embed_Unity_Exe_Winforms_Control();
// Add the interop host control to the Grid
// control's collection of child controls.
Grid_To_Embed_Winforms_Control_In.Children.Add(host);
Already_Loaded = true;
}
}
}
Do not forget to add using System.Windows.Forms.Integration; on top. Please note that I added the boolean Already Loaded to make sure that when the viewport changes (you want to view another page of your WPF application) the process is not started again. In my complete solution, I make use of Microsoft Dependency Injection and this control is in a viewmodel which is added as a singleton. In this way, I only start the process once.
So this is it, this worked for me.
What is better than other solutions I saw while googling:
The resizing is working better and done "automatically", I did not need to invoke a resize-method myself (except for the one in Form1.cs) .
I can also have control and focus over the Unity.
PS: If you want to make it look nicer and more "embedded", you can do the following things with the Winform-control:
Select splitcontainer1 and go to Properties
Set BorderStyle to 'None'
Set Panel2Collapsed to 'True'
I have searched the internet far and wide and seen many questions like this, but I have not seen an actual answer.
I have a rich text box control with lots of text in it. It has some legal information in this control. By default the "Accept" button is disabled. I want to detect on the scroll event if the position of the v-scroll bar is at the bottom. If it is at the bottom, enable the button.
How would I detect the current v-scroll bar position?
Thank You!
EDIT
I am using WinForms (.Net 4.0)
This should get you close to what you are looking for. This class inherits from the RichTextBox and uses some pinvoking to determine the scroll position. It adds an event ScrolledToBottom which gets fired if the user scrolls using the scrollbar or uses the keyboard.
public class RTFScrolledBottom : RichTextBox {
public event EventHandler ScrolledToBottom;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20A;
private const int WM_USER = 0x400;
private const int SB_VERT = 1;
private const int EM_SETSCROLLPOS = WM_USER + 222;
private const int EM_GETSCROLLPOS = WM_USER + 221;
[DllImport("user32.dll")]
private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
public bool IsAtMaxScroll() {
int minScroll;
int maxScroll;
GetScrollRange(this.Handle, SB_VERT, out minScroll, out maxScroll);
Point rtfPoint = Point.Empty;
SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref rtfPoint);
return (rtfPoint.Y + this.ClientSize.Height >= maxScroll);
}
protected virtual void OnScrolledToBottom(EventArgs e) {
if (ScrolledToBottom != null)
ScrolledToBottom(this, e);
}
protected override void OnKeyUp(KeyEventArgs e) {
if (IsAtMaxScroll())
OnScrolledToBottom(EventArgs.Empty);
base.OnKeyUp(e);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL) {
if (IsAtMaxScroll())
OnScrolledToBottom(EventArgs.Empty);
}
base.WndProc(ref m);
}
}
This is then how it can get used:
public Form1() {
InitializeComponent();
rtfScrolledBottom1.ScrolledToBottom += rtfScrolledBottom1_ScrolledToBottom;
}
private void rtfScrolledBottom1_ScrolledToBottom(object sender, EventArgs e) {
acceptButton.Enabled = true;
}
Tweak as necessary.
The following works very well in one of my solutions:
Point P = new Point(rtbDocument.Width, rtbDocument.Height);
int CharIndex = rtbDocument.GetCharIndexFromPosition(P);
if (rtbDocument.TextLength - 1 == CharIndex)
{
btnAccept.Enabled = true;
}
The question How to get scroll position for RichTextBox? could be helpful, Check out this function
richTextBox1.GetPositionFromCharIndex(0);
am trying to simulate mouse right click in my WinForm app as follow :
public const int WM_RBUTTONDOWN = 0x204;
public const int WM_RBUTTONUP = 0x205;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
public static IntPtr MakeLParam(int LoWord, int HiWord)
{
return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
}
public static void Click(int x, int y)
{
SendMessage(this.Handle, WM_RBUTTONDOWN, IntPtr.Zero, MakeLParam(x,y));
SendMessage(this.Handle, WM_RBUTTONUP, IntPtr.Zero, MakeLParam(x,y));
}
(x,y) the coords inside the app window form, but nothing happens what am missing here?
EDIT: I have also tried FindWindow(null,"Form1"); and it gives same Handle as this.Handle..
Why you dont use the Events built-in in Windows Forms??
Use de MouseDown Event of any control, example the Form:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
}
}
The MouseDown Event take the control in a any mouse button pressed.