Last week I needed to write a snipping tool on the product. I found that the articles on the blog only had the step of capturing the screen, or required following an official account for marketing, so I wrote one myself. I found an open source framework on github today. If you are ready to start writing, you can refer to that framework.

https://github.com/WPFDevelopersOrg/WPFDevelopers

I just finished writing it and recorded the problems I encountered to prevent forgetting them later.

@[toc]

Error when rendering canvas: System.ArgumentException: “Value does not fall within the expected range.”

1
2
3
4
5
6
7
8
9
10
var renderTargetBitmap = new RenderTargetBitmap(Convert.ToInt32(_AllScreenWidth),
Convert.ToInt32(_AllScreenHeight), 96d, 96d, PixelFormats.Default);
renderTargetBitmap.Render(Capture_Canvas);


var targetRec = new Int32Rect((int)rect.X, (int)rect.Y, (int)rect.Width , (int)rect.Height );

var img = new CroppedBitmap(renderTargetBitmap,
targetRec);

Here, I originally set the rendering range according to the user frame and found an error reported. After modifying the X and Y of targetRec, the picture can be displayed. confirming that it should be the reason for the cropping range. Modifying it to render the entire screen solved the problem.

The reason for the error should be that the clipping range is consistent with the rendering range, causing an error. The original image should be guaranteed to be larger than the clipping area.

Cancel all events mounted on EventHandler

How would it be possible to remove all event handlers of the ‘Click’ event of a ‘Button’?

First of all, I actually took a detour in this step and increased the redundancy of the program. I should have created a factory pattern. When the user clicks the button, different functions are awakened according to the nature. This can be referred to the framework address above.

It was because I created four buttons under the screenshot, namely arrow, rectangle, text and save. But after waking up the arrow function, clicking the rectangle again would do both, but their click order was inconsistent, so I planned to cancel all events mounted on MouseDown, MouseUp, MouseMove every time I clicked the event button, and then re-mount to this button event, so there was the above requirement.
After searching, I found that I need to call the removeHanlder method in MouseDownEventHandler by reflection method. This can be seen in the metadata, but all current solutions are for Winform, not applicable to WPF.

This is Winform control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/// <summary>
/// Clear the delegate linked to an event of an object
/// </summary>
/// <param name="ctrl">Control Object</param>
/// <param name="eventName">Event Name, Default</param>
public static void ClearEvents(this object ctrl, string eventName = "_EventAll")
{
if (ctrl == null) return;

BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static;
EventInfo[] events = ctrl.GetType().GetEvents(bindingFlags);


if (events == null || events.Length < 1) return;

for (int i = 0; i < events.Length; i++)
{

EventInfo ei = events[i];
//Only delete the specified method, default is _EventAll, adding _ in front is to distinguish from system, prevent similarity later
if (eventName != "_EventAll" && ei.Name != eventName) continue;



/********************************************************
* Each event of the class corresponds to a private delegate class
* type member variable with the same name (changed, added Event prefix in front) (this can be confirmed with Reflector). Because private member
* variables cannot be modified in the base class, so in order to get the
* event declared in the base class, get the FieldInfo of the member variable
* corresponding to the event from the DeclaringType of EventInfo and modify it
********************************************************/

var fileds = ei.DeclaringType.GetFields(bindingFlags);
foreach (var filed in fileds)
{
if (filed.Name.Equals("MouseDownEvent"))
{
filed.SetValue(ctrl, null);
var mEvent = filed.GetValue(ctrl);
var remove = filed.FieldHandle;

}
if (filed.Name.Equals("MouseMoveEvent"))
filed.SetValue(ctrl, null);
if (filed.Name.Equals("MouseUpEvent"))
filed.SetValue(ctrl, null);
}
}

This is WPF UIelement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/// <summary>
/// Removes all event handlers subscribed to the specified routed event from the specified element.
/// </summary>
/// <param name="element">The UI element on which the routed event is defined.</param>
/// <param name="routedEvent">The routed event for which to remove the event handlers.</param>
public static void RemoveRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
{
// Get the EventHandlersStore instance which holds event handlers for the specified element.
// The EventHandlersStore class is declared as internal.
var eventHandlersStoreProperty = typeof(UIElement).GetProperty(
"EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);

// If no event handlers are subscribed, eventHandlersStore will be null.
// Credit: https://stackoverflow.com/a/16392387/1149773
if (eventHandlersStore == null)
return;

// Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance
// for getting an array of the subscribed event handlers.
var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod(
"GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(
eventHandlersStore, new object[] { routedEvent });

if (routedEventHandlers == null)
return;

// Iteratively remove all routed event handlers from the element.
foreach (var routedEventHandler in routedEventHandlers)
element.RemoveHandler(routedEvent, routedEventHandler.Handler);
}

Arrow Style

The arrow style is also a framework found on github, I will post the address directly

https://github.com/icsharpcode/WpfDesigner