So i'm writing a little tool to have a bit easier access to functions because there are no hotkeys.
For now i use global hotkeys to move my mouse to specific locations on the monitor and perform r/l clicks.
KeyboardHook hook = new KeyboardHook();
hook.KeyPressed +=
new EventHandler<KeyPressedEventArgs>(hook_KeyPressed);
hook.RegisterHotKey(TestBox.ModifierKeys.Control, Keys.D1);
void hook_KeyPressed(object sender, KeyPressedEventArgs e)
{
Process[] p = Process.GetProcessesByName(".......");
if (p.Count() > 0)
SetForegroundWindow(p[0].MainWindowHandle);
RightMouseClick(2000,110);
System.Threading.Thread.Sleep(200);
LeftMouseClick(2060,120);
}
So far so good.
Now my problem:
The coordinates are the ones from my right monitor (triple setup). But what if i decide to switch the programm to my left monitor? Then the coordinates wont match anymore.
My way would be something like this, but i don't know how to start and implement this.
If you first press the hotkey, there is a check for coordinates. If not -> ask to set them.
User then moves mouse to first point and press F2, move to second point and press F2 again.
So the coordinates get saved and the hotkey can be performed.
Your question is about screen coordinates on a multi-screen display with emphasis on:
[...] what if i decide to switch the programm to my left monitor?
Have you considered using the Screen class to get the display metrics? Using Jeremy's link for GetWindowRect() you could have your app poll for program position changes at intervals, determine which screen each app is on, and adjust your coordinates accordingly.
Screen wrapper for ComboBox
class ScreenItem
{
public ScreenItem(Screen screen) => Screen = screen;
public Screen Screen { get; }
string _displayName = null;
public string DisplayName
{
get => _displayName?? Screen.DeviceName.Substring(Screen.DeviceName.LastIndexOf('\\') + 1);
set => _displayName = value;
}
public override string ToString()
{
List<string> builder= new List<string>();
builder.Add($"{nameof(Screen.Primary)} : {Screen.Primary}");
builder.Add($"{nameof(Screen.Bounds)} : {Screen.Bounds}");
builder.Add($"{nameof(Screen.WorkingArea)} : {Screen.WorkingArea}");
return
string.Join(Environment.NewLine, builder.ToArray()
.Select(_ => $"\t{_}"))
+ Environment.NewLine;
}
}
Enumerate
public MainForm()
{
InitializeComponent();
ScreenItem primary = null;
foreach (var screenItem in Screen.AllScreens.Select(_ => new ScreenItem(_)))
{
if(screenItem.Screen.Primary)
{
primary = screenItem;
}
richTextBox.SelectionColor = Color.Navy;
richTextBox.AppendText($"{screenItem.DisplayName}{Environment.NewLine}");
richTextBox.SelectionColor = ForeColor;
richTextBox.AppendText($"{screenItem}{Environment.NewLine}");
}
}
Related
I cant find any information for this...
Basically I have a TopMost WinForm in C# that is an overlay for another application.
The overlay has buttons that I need to be able to press without stealing focus from the other application.
Is this possible, as I can't find any relative information.
You could store the mouse position:
Point point = Cursor.Position;
and then use an area with no controls in it to change the focus back to the DirectX9 window,
moving the cursor back to the original position before clicking again? That might work.
The only issue is that the button would still be there so you would need some way of getting it to click to the window rather than the button.
e.g.
Point p = Cursor.Position;
Cursor.Position = new point(x,y);
mouse(leftClick);
Cursor.Position = p;
mouse(leftClick);
the mouse(leftclick) method is here.
The other way to do this would be to track cursor position separately and then on each click, check if the click is within any controls and if so then run that method;
(Please tell me in the comments if there is a way to do this more efficiently as it would actually be quite useful to know)
Here is a solution that is working for me:
It identifies the target program by its title or parts of it and after each Button click it sets the target to be the foreground window again.
I can type into notepad, click a Button and type on..
I start by making the Form click-through.
You may want to do a lot more styling, maybe maximize, remove the control/min/max boxes and the title or even grab the target's position and size and overlay just the target window. This way you can make completely unobtrusive adornments to the target.
(I did that once because the target didn't treat touch screens right; so I made an overlay with holes in its region where the non-touch-defunct controls were. Nobody can even notice that the overlay is there!)
I write a few status info into to the output console..
Instead of the MainWindowTitle you could also use the ProcessName to identify the target. You can also incorporate code to re-establish the process after an error, like the user closing and restarting the target app..
using System.Runtime.InteropServices;
using System.Diagnostics;
...
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
string targetTitle = "Notepad2";
Process theProcess = null;
public Form1()
{
InitializeComponent();
// make the form clickthrough..
TransparencyKey = Color.Fuchsia;
BackColor = TransparencyKey;
// find and keep a reference to the target process
theProcess = findProcess(targetTitle);
// our overlay should stay on top
TopMost = true;
}
Process findProcess(string processTitle)
{
Process[] processList = Process.GetProcesses();
return processList.FirstOrDefault(
pr => pr.MainWindowTitle.ToLower().Contains(targetTitle.ToLower()));
}
void setActive(Process theProcess)
{
if (theProcess == null)
{ Console.WriteLine( "Process " + targetTitle + " not found";) return; }
bool ok = SetForegroundWindow(theProcess.MainWindowHandle);
Console.Write("Process " + theProcess.ProcessName + " (" +
theProcess.MainWindowTitle + + ok ? " OK." : " not OK!" );
}
private void button1_Click(object sender, EventArgs e)
{
// do your stuff..
Console.WriteLine("Testing Button 1");
// done: bring the target process back:
setActive(theProcess);
}
}
This will probably sound strange for some of you, but I can not figure out right way to read joystick input without blocking my UI form. I have found this example on internet:
static void Main()
{
// Initialize DirectInput
var directInput = new DirectInput();
// Find a Joystick Guid
var joystickGuid = Guid.Empty;
foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad,
DeviceEnumerationFlags.AllDevices))
joystickGuid = deviceInstance.InstanceGuid;
// If Gamepad not found, look for a Joystick
if (joystickGuid == Guid.Empty)
foreach (var deviceInstance in directInput.GetDevices(DeviceType.Joystick,
DeviceEnumerationFlags.AllDevices))
joystickGuid = deviceInstance.InstanceGuid;
// If Joystick not found, throws an error
if (joystickGuid == Guid.Empty)
{
Console.WriteLine("No joystick/Gamepad found.");
Console.ReadKey();
Environment.Exit(1);
}
// Instantiate the joystick
var joystick = new Joystick(directInput, joystickGuid);
Console.WriteLine("Found Joystick/Gamepad with GUID: {0}", joystickGuid);
//Query all suported ForceFeedback effects
var allEffects = joystick.GetEffects();
foreach (var effectInfo in allEffects)
Console.WriteLine("Effect available {0}", effectInfo.Name);
//Set BufferSize in order to use buffered data.
joystick.Properties.BufferSize = 128;
// Acquire the joystick
joystick.Acquire();
// Poll events from joystick
while (true)
{
joystick.Poll();
var datas = joystick.GetBufferedData();
foreach (var state in datas)
Console.WriteLine(state);
}
}
I have installed sharpdx and I can see joystick axis output in console.
Now I want to display each axis data in separate textbox on my UI. So I changed few lines of code:
joystick.Poll();
var data = joystick.GetBufferedData();
foreach (var state in data)
{
if (state.Offset == JoystickOffset.X)
{
textBox1.Text = state.Value.ToString();
}
}
So this should pass data to textbox. If I write here Console.Writeline(state.Value) I will get data I want to display. Problem is, that this while loop blocks UI.
I would like to put this while loop in private void timer_tick and set maybe 10 milliseconds refresh rate. But I don't know how to access variables like var joystick in separate function. I have also read that this can be done with asynchronous programming. Sadly I am stuck now.
Can anybody help me with as simple solution as possible to this problem? Code example would be great.
Suppose you code with WinForm (deduced from blocking my UI form in your question).
Add a timer to your Form from the designer.
And add these lines in the Form's .ctor:
public partial class JoyStickForm : Form
{
public JoyStickForm()
{
InitializeComponent();
// ...Other initialization code has been stripped...
// Instantiate the joystick
var joystick = new Joystick(directInput, joystickGuid);
//Console.WriteLine("Found Joystick/Gamepad with GUID: {0}", joystickGuid);
timer1.Interval = 100;
timer1.Tick += (s, e) =>
{
joystick.Poll();
var lastState = joystick.GetBufferedData().Last(); //only show the last state
if (lastState != null)
{
if (lastState.Offset == JoystickOffset.X)
{
textBox1.Text = lastState.Value.ToString();
}
}
};
//start the timer
timer1.Enabled = true;
}
}
Explanations:
A refreshing rate of 100 times per second is unnecessarily fast, remember the refreshing rate of most monitors is about 60 Hz. I set it as 100 ms (10 refreshes per second), which is acceptable.
Using lambda expression, you have access to the variables like var joystick from the timer's tick handler.
In each freshing, only the last state in the data is displayed to the TextBox.
I have a Canvas control with various elements on, in this particular function I am allowing a user to drag the end point of a line around the canvas. In the MouseMove function I call e.GetPosition().
The function is, according to the VS performance analyzer, close to 30% of total CPU for the app when constantly moving around. Its pretty slow. What can I do to increase this performance?
CurrentPoint = e.GetPosition(PointsCanvas);
I've faced the same problem while using MouseMove on windows phone 8. It seems that while dragging , events (containing the coordinates you need ) are raised at regular time interval ( depending on what you do in the implementation in your listeners, every 20 ms for example). So what I did was to populate a Queue with my coordinates and create a Thread that consume that Queue by enqueue the first element and do the logic I want. Like that the logic is not done serially because it's another thread who does the job.
I don't know if I'm enough clear so please take a look to the code below :
//Class used to store e.getPosition(UIElement).X/Y
public class mouseInformation
{
public int x { get; set; }
public int y { get; set; }
public mouseInformation(int x, int y, String functionName)
{
this.x = x;
this.y = y;
}
}
private readonly Queue<mouseInformation> queueOfEvent = new Queue<mouseInformation>();
//MouseMove listener
private void wpCanvas_MouseDragged(object sender, System.Windows.Input.MouseEventArgs e)
{
//Instead of "wpCanvas" put the name of your UIElement (here your canvas name)
mouseInformation mouseDragged = new mouseInformation((int)e.GetPosition(wpCanvas).X, (int)e.GetPosition(wpCanvas).Y);
EnqueueMouseEvent(mouseDragged);
}
//Allow you to add a MouseInformation object in your Queue
public void EnqueueMouseEvent(mouseInformation mi)
{
lock (queueOfEvent)
{
queueOfEvent.Enqueue(mi);
Monitor.PulseAll(queueOfEvent);
}
}
//Logic that your consumer thread will do
void Consume()
{
while (true)
{
mouseInformation MI;
lock (queueOfEvent)
{
while (queueOfEvent.Count == 0) Monitor.Wait(queueOfEvent);
MI = queueOfEvent.Dequeue();
}
// DO YOUR LOGIC HERE
// i.e DoSomething(MI.x, MI.y)
}
}
And don't forget to create the thread in your Main() or in MainPage_Loaded(object sender, RoutedEventArgs e) method if you are Windows phone user.
System.Threading.ThreadStart WatchQueue = new System.Threading.ThreadStart(Consume);
System.Threading.Thread RunWatchQueue = new System.Threading.Thread(WatchQueue);
RunWatchQueue.Name = "Events thread";
RunWatchQueue.Start();
To be simple less you do in your MouseMove listener, more speed it will be.
You can aswell do the logic asynchronously or even use Bresenham algorithm to simulate more events.
Hope it helps.
Are you using any effects such as dropshaddow etc?
I recently had the situation where e.GetPosition() was also using 30% of the app's cpu resources, which doesn't make any sense right?
I turns out that up the visual tree there was a control applying a dropshaddow effect and that was what was slowing everything down so much...
For my programming class we are required to make a memory match game. When the player clicks on a panel an image is displayed. The player clicks on two panels, if the images match they are removed, if they are different they are turned face down.
The problem I am having with this is that only the image from the first panel gets displayed, even though I am using the same line of code to display the images from both panels, and use Thread.Sleep to pause the program after the second tile has been picked. I don't understand why this is happening, any help would be appreciated.
private void tile_Click(object sender, EventArgs e)
{
string tileName = (sender as Panel).Name;
tileNum = Convert.ToInt16(tileName.Substring(5)) - 1;
//figure out if tile is locked
if (panelArray[tileNum].isLocked == false)
{
pickNum++;
//although the following line of code is used to display the picture that is stored in the tile array
//what is happening is that it will only display the picture of the first tile that has been picked.
//when a second tile is picked my program seems to ignore this line completely, any ideas?
panelArray[tileNum].thisPanel.BackgroundImage = tiles[tileNum].tileImage;
if (pickNum == 1)
{
pick1 = tileNum;
panelArray[tileNum].isLocked = true;
}
else
{
pick2 = tileNum;
UpdateGameState();
}
}
}
private void UpdateGameState()
{
Thread.Sleep(1500);
if (tiles[pick1].tag == tiles[pick2].tag)//compares tags to see if they match.
{
RemoveTiles();
}
else
{
ResetTiles();
}
pickNum = 0;
guess += 1;
guessDisplay.Text = Convert.ToString(guess);
if (correct == 8)
{
CalculateScore();
}
}
try this:
(sender as Panel).BackgroundImage = tiles[tileNum].tileImage;
you have to be sure that your tile_Click method is associated to both panel...
So I have 2 controls on my main form, a custom hexbox control and a richtextbox as in the picture below (It wouldnt let me post an image so will have to follow the link). What I would like to do is depending on where the user has clicked in the hexbox control, the rich textbox on the right will scroll to an associated line.
http://i.stack.imgur.com/8PfRt.jpg
The current issue I'm having is that the code to handle the hexbox click is contained in a separate class (hexbox.cs) and so I'm not able to then take the location of the click and scroll to the associated line in the richtextbox which is contained in the mainform class. I attempted to create a new instance of the mainform to access the richtextbox but obviously this results is the creation of a new richtexbox with none of the original content.
Heres the code in the hexbox class that deals with a mouse click if its any help:
void SetCaretPosition(Point p)
{
System.Diagnostics.Debug.WriteLine("SetCaretPosition()", "HexBox");
if (_byteProvider == null || _keyInterpreter == null)
return;
long pos = _bytePos;
int cp = _byteCharacterPos;
if(_recHex.Contains(p))
{
BytePositionInfo bpi = GetHexBytePositionInfo(p);
pos = bpi.Index;
cp = bpi.CharacterPosition;
SetPosition(pos, cp);
ActivateKeyInterpreter();
UpdateCaret();
Invalidate();
}
else if(_recStringView.Contains(p))
{
BytePositionInfo bpi = GetStringBytePositionInfo(p);
pos = bpi.Index;
cp = bpi.CharacterPosition;
SetPosition(pos, cp);
ActivateStringKeyInterpreter();
UpdateCaret();
Invalidate();
}
}
If I undersntand properly, this should help.
If you have access to hexbox class, you can expose event (MouseClick - or similar) - or even create your own with args prividig selected line - subscribe to this event in your Form.
Then when user click your form class'll be notified .
class HexBox : UserControl{
// ..
}
public class MyForm :Form{
public MyForm(){
HexBox hexBox = new HexBox();
Controls.Add(hexBox);
hexBox.MouseDown += (sender, args) =>{
// call your scroll to function
};
InitializeComponent();
}
}