Custom Avalonia controll to render from other thread performance problem - c#

I have a background thread, which renders some image to WriteableBitmap.
I'm making a custom control, which will use this writeablebitmap and update every frame to make animation.
Unfortunately, now forcing this control to InvalidateVisual().
That worked, but the performance is pretty bad.
On the main window I subscribed to Renderer.SceneInvalidated to log some framerates.
this.Renderer.SceneInvalidated += (s, e) =>
{
_logs.Add((DateTime.Now - _prev).Ticks);
_prev = DateTime.Now;
};
And found that about 7%-10% frames are rendered in 30ms, and others 90% in 16ms.
So I'm looking for a better performance solution with this problem.

You can utilize custom drawing operation API with direct access to SKCanvas on the render thread.
public class CustomSkiaPage : Control
{
public CustomSkiaPage()
{
ClipToBounds = true;
}
class CustomDrawOp : ICustomDrawOperation
{
public CustomDrawOp(Rect bounds)
{
Bounds = bounds;
}
public void Dispose()
{
// No-op in this example
}
public Rect Bounds { get; }
public bool HitTest(Point p) => false;
public bool Equals(ICustomDrawOperation other) => false;
public void Render(IDrawingContextImpl context)
{
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
// draw stuff
}
}
public override void Render(DrawingContext context)
{
context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height)));
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
}
}

Related

Block mouse bubble in Xamarin Mac

I have created a custom NSView that i would like to place over the top of the content of a window to block any interaction while all the content is loading. The problem i was having is that i could click through the NSView to the controls below though that has now been fixed. The new problem is that even though i cannot click on the controls, when i move the mouse over text controls, the mouse switches to the I Beam icon.
How do i make the NSView completely block all interaction with everything below it?
The NSView i created is below:
[Register("StupidView")]
public class StupidView : NSView
{
public StupidView()
{
// Init
Initialize();
}
public StupidView(IntPtr handle) : base (handle)
{
// Init
Initialize();
}
[Export("initWithFrame:")]
public StupidView(CGRect frameRect) : base(frameRect) {
// Init
Initialize();
}
private void Initialize()
{
this.AcceptsTouchEvents = true;
this.WantsLayer = true;
this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}
public override void DrawRect(CGRect dirtyRect)
{
var ctx = NSGraphicsContext.CurrentContext.GraphicsPort;
ctx.SetFillColor(new CGColor(128, 128, 128, 0.7f));
ctx.FillRect(dirtyRect);
}
public override void MouseDown(NSEvent theEvent)
{
if (Hidden)
{
base.MouseDown(theEvent);
}
}
public override void MouseDragged(NSEvent theEvent)
{
if (Hidden)
{
base.MouseDragged(theEvent);
}
}
public override bool AcceptsFirstResponder()
{
return !this.Hidden;
}
public override bool AcceptsFirstMouse(NSEvent theEvent)
{
return !this.Hidden;
}
public override NSView HitTest(CGPoint aPoint)
{
return Hidden ? null : this;
}
}
I had the same problem a few weeks ago, and here is how I could manage this :
First, to prevent user interactions on the superview placed below, I added a transparent button which was there only to catch the mouse click and, if you don't have to do anything, do nothing :
private void Initialize()
{
this.AcceptsTouchEvents = true;
this.WantsLayer = true;
this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
//Add button to prevent user interactions
NSButton buttonToPreventUserInteraction = new NSButton();
buttonToPreventUserInteraction.Bordered = false;
buttonToPreventUserInteraction.Transparent = true;
buttonToPreventUserInteraction.TranslatesAutoresizingMaskIntoConstraints = false;
AddSubview(buttonToPreventUserInteraction);
//If you want to add some constraints on the button, for it to resize and keep the same size of your subview
var dicoViews = new NSMutableDictionary();
dicoViews.Add((NSString)"buttonToPreventUserInteraction", buttonToPreventUserInteraction);
NSLayoutConstraint[] buttonToPreventUserInteractionHorizontalConstraints = NSLayoutConstraint.FromVisualFormat("H:|[buttonToPreventUserInteraction]|", NSLayoutFormatOptions.DirectionLeadingToTrailing, null, dicoViews);
NSLayoutConstraint[] buttonToPreventUserInteractionVerticalConstraints = NSLayoutConstraint.FromVisualFormat("V:|[buttonToPreventUserInteraction]|", NSLayoutFormatOptions.DirectionLeadingToTrailing, null, dicoViews);
AddConstraints(buttonToPreventUserInteractionHorizontalConstraints);
AddConstraints(buttonToPreventUserInteractionVerticalConstraints);
}
For your other problem, which is the mouse cursor changing from the content in your superview placed below, you can add a NSTrackingArea on your subview, and implement the override method "MouseMoved" to change the cursor. You can do something like this :
First Add the NSTrackingArea on your subview (you can put this code in your "Initialize" method)
NSTrackingAreaOptions opts = ((NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.InVisibleRect));
var trackingArea = new NSTrackingArea(new CGRect(0, 0, FittingSize.Width, FittingSize.Height), opts, Self, null);
AddTrackingArea(trackingArea);
And then implement the override method :
public override void MouseMoved(NSEvent theEvent)
{
//You can choose the type of cursor you want to use here
NSCursor.ArrowCursor.Set();
}
This made it for me, hope it will for you too

how to call Resize Event in C#

I'm trying to create new Button with custom event. It's new for me. I'm trying to call "Resize". I wanna create "switch" like in android.
I'm trying to do this like other existing controls. I've been doing this for 2 days and i still have nothing. I belive that you will able to help me :)
Here is my code:
public abstract class SwitchBase : Control
{
private Button first;
private Button second;
public SwitchBase()
{
InitializeMySwitch();
}
private void InitializeMySwitch()
{
Controls.Add(first = new Button());
Controls.Add(second = new Button());
//first
first.Text = "first";
//second
second.Text = "second";
second.Location = new System.Drawing.Point(first.Location.X + first.Width, first.Location.Y);
}
public delegate void ChangedEventHandler(object source, EventArgs args);
public event ChangedEventHandler Changed;
protected virtual void OnSwitchChanged()
{
if (Changed != null)
Changed(this, EventArgs.Empty);
}
public delegate void ResizeEventHandler(object source, EventArgs args);
public event ResizeEventHandler Resize;
protected virtual void OnResize()
{
Resize(this, EventArgs.Empty);
}
}
public class Switch : SwitchBase
{
public Switch()
{
}
protected override void OnSwitchChanged()
{
base.OnSwitchChanged();
}
protected override void OnResize()
{
base.OnResize();
}
}
In another button I change the size of my switch
From reading your code, I gather that by "call Resize" you mean to raise the event. What you are doing is correct... although it should be noted that by the default event implementation, it will be null if there are no subscribers...
In fact, another thread could be unsubscribing behind your back. Because of that the advice is to take a copy.
You can do that as follows:
var resize = Resize;
if (resize != null)
{
resize(this, EventArgs.Empty)
}
It should be noted that the above code will call the subscribers to the event, but will not cause the cotrol to resize. If what you want is to change the size of your control, then do that:
this.Size = new Size(400, 200);
Or:
this.Width = 400;
this.Height = 200;
Note: I don't know what Control class you are using. In particular, if it were System.Windows.Forms.Control it already has a Resize event, and thus you won't be defining your own. Chances are you are using a Control class that doesn't even have Size or Width and Height.
Edit: System.Web.UI.Control doesn't have Resize, nor Size or Width and Height. But System.Windows.Controls.Control has Width and Height even thought it doesn't have Resize.

Semantic Zoom for custom view hierarchy instead of plain grid- or listview

I have a page layout in xaml containing a grid with several gridviews which represent different contents and individual styles.
This is the hub of my application that presents these different contents like for example:
artists, performances, records that are related somehow but different in content and therefore represented differently. ( entirely different itemtemplates and groupingstylefor each )
I want to Implement a semantic zoom that once zoomed out should show the custom made groups i have. So it should show artists, performances, recordings as groups when zoomed out.
Unfortunally i can only put a single GridView or ListView inside the ZoomedIn/Out tags.
Does anyone know how to work around this problem or can deliver a solid solution?
I fould a solution, it works, but it's yet rather clumsy. (I didn't have enough time to look into it deeply enough.)
I would actually appreciate it if someone would suggest a proper (reusable, truly object oriented :-), etc.) solution.
You have to implement the ISemanticZoomInformation interface in a new class.
I didn't find a really detailed description about how the interface works, so I had to try a lot. My problem was, that a scrollViewer is needed to be able to jump to a certain point in the zoomedIn view, when you tap on a tile in the zoomedOutView. I couldn't subclass from the scrollViewer class. Probably you'd need to subclass a subclass of GridView, that has a scrollViewer as a child (if that's at all possible). In my solution it is (very wrongly) assumed, that it has a scrollViewer child. The other problem was, that if you pinch, the MakeVisible method is getting called and item.Bounds.Left will be 0.5 (and in this case you don't want to scroll in the zoomedIn view anywhere), but if you tap on an element in the zoomedOut view, you have to set this value in your code and in this case, you want to scroll the scrollViewer in the MakeVisible method.
E.g.:
public class GridWithSemanticZoomInformation : Grid , ISemanticZoomInformation
{
private SemanticZoom _semanticZoomOwner;
private Boolean _IsZoomedInView;
private Boolean _IsActiveView;
public void CompleteViewChange()
{
//
}
public void CompleteViewChangeFrom(SemanticZoomLocation source, SemanticZoomLocation destination)
{
this.IsActiveView = false;
}
public void CompleteViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination)
{
this.IsActiveView = true;
}
public void InitializeViewChange()
{
//
}
public bool IsActiveView
{
get
{
return this._IsActiveView;
}
set
{
this._IsActiveView = value;
}
}
public bool IsZoomedInView
{
get
{
return this._IsZoomedInView;
}
set
{
this._IsZoomedInView = value;
}
}
public void MakeVisible(SemanticZoomLocation item)
{
this.SemanticZoomOwner.IsZoomedInViewActive = (this.Equals(this.SemanticZoomOwner.ZoomedInView));
if (item.Bounds.Left != 0.5)
{
if (this.Children.Count() == 1)
{
foreach (UIElement element in this.Children)
{
if (element.GetType() == typeof(ScrollViewer))
{
((ScrollViewer)element).ScrollToHorizontalOffset(item.Bounds.Left);
}
}
}
}
}
public SemanticZoom SemanticZoomOwner
{
get
{
return this._semanticZoomOwner;
}
set
{
this._semanticZoomOwner = value;
}
}
public void StartViewChangeFrom(SemanticZoomLocation source, SemanticZoomLocation destination)
{
//
}
public void StartViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination)
{
//
}
}
I wrote some clumsy event handlers for the cases, when you tap on the items in the zoomedOut view as well:
private void FirstButton_Tapped(object sender, TappedRoutedEventArgs e)
{
this.ZoomedOutGrid.SemanticZoomOwner.ToggleActiveView();
SemanticZoomLocation moveTo = new SemanticZoomLocation();
moveTo.Bounds = new Rect(0, 0, 0, 0);
this.ZoomedOutGrid.InitializeViewChange();
this.ZoomedInGrid.InitializeViewChange();
this.ZoomedOutGrid.StartViewChangeFrom(new SemanticZoomLocation(), new SemanticZoomLocation());
this.ZoomedInGrid.StartViewChangeTo(new SemanticZoomLocation(), moveTo);
this.ZoomedInGrid.MakeVisible(moveTo);
this.ZoomedOutGrid.CompleteViewChangeFrom(new SemanticZoomLocation(), new SemanticZoomLocation());
this.ZoomedInGrid.CompleteViewChangeTo(new SemanticZoomLocation(), new SemanticZoomLocation());
this.ZoomedOutGrid.CompleteViewChange();
this.ZoomedInGrid.CompleteViewChange();
}
private void SecondButton_Tapped(object sender, TappedRoutedEventArgs e)
{
SemanticZoomLocation moveTo = new SemanticZoomLocation();
moveTo.Bounds = new Rect(270, 0, 0, 0);
this.ZoomedOutGrid.InitializeViewChange();
this.ZoomedInGrid.InitializeViewChange();
this.ZoomedOutGrid.StartViewChangeFrom(new SemanticZoomLocation(), new SemanticZoomLocation());
this.ZoomedInGrid.StartViewChangeTo(new SemanticZoomLocation(), moveTo);
this.ZoomedInGrid.MakeVisible(moveTo);
this.ZoomedOutGrid.CompleteViewChangeFrom(new SemanticZoomLocation(), new SemanticZoomLocation());
this.ZoomedInGrid.CompleteViewChangeTo(new SemanticZoomLocation(), moveTo);
this.ZoomedOutGrid.CompleteViewChange();
this.ZoomedInGrid.CompleteViewChange();
}

Graphical glitch occuring in XNA on Windows Phone 7 in Landscape orientation

I've been working on a cross-platform framework/abstraction-layer for a number of months now, based on the XNA framework for WP7 and Mono for Android and iOS. Having got the majority of functionality working for Android, I have recently returned to WP7 in order to get a simple game working on both platforms.
The trouble is, I've encountered a very strange graphical error. The error does not occur in a Windows-based XNA game, and only occurs on WP7 when the orientation is Landscape (either by default or specified). The glitch appears on the emulator, but not the physical device. For testing purposes, I am displaying a single image on a red background.
Windows XNA
Windows http://www.genius-i.com/images/Windows.png
Windows Phone 7 (Portrait)
Portait http://www.genius-i.com/images/Portrait.png
Windows Phone 7 (Landscape)
Landscape http://www.genius-i.com/images/Landscape.png
Obviously the landscape image is not coming out as expected. While it's all handled through an abstraction layer, the XNA platform files are identical, the only difference between the Windows and WP7 versions being the target platform/framework.
Similarly, the calls to the graphics device and renderer are identical.
During Construction ( Extracted from various classes and functions )
device = new Microsoft.Xna.Framework.GraphicsDeviceManager(game);
device.PreferredBackBufferHeight = 480;
device.PreferredBackBufferWidth = 800;
Backend.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
// Whether or not I set the orientation, the glitchoccurs.
During Initialisation ( Extracted from various classes and functions )
renderer = new SpriteBatch(device.GraphicsDevice);
During Drawing
device.Clear(Color.Red);
renderer.Begin();
renderer.Draw(ZNGTexture, new Rectangle(10, 400, 145, 66), null, Color.White, 0f, Vector.Zero, SpriteEffects.none, 0f);
renderer.End()
Does anyone have any insight into what might cause a graphical glitch such as the one I've described?
Thanks for any help you might offer, and sorry to intrude with a second question over the course of a few days.
Full Code
To help in identifying the issue, I'm presenting a good deal of the actual code. I originally excluded it because it's a bit long, and the classes are simple wrappers for most of XNA's functionality.
Game Class
public class Game : Microsoft.Xna.Framework.Game, IGame
{
#region Data: Fields
private GraphicsBackendXNA graphics;
private Renderer2DXNA renderer;
ContentLoaderXNA content;
#endregion
#region Data: Properties
public GraphicsBackend Backend { get { return graphics; } }
public Renderer2D Renderer { get { return renderer; } }
public ContentLoader Loader { get { return content; } }
#endregion
#region Methods: Construction
public Game()
{
graphics = new GraphicsBackendXNA(this);
content = new ContentLoaderXNA(this.Services, "Content");
FinaliseConstruction();
}
protected virtual void FinaliseConstruction() { }
#endregion
#region Methods: Initialisation
protected override void Initialize()
{
Core.Console.Initialise(); // Initialised once for error logging.
base.Initialize();
renderer = new RendererXNA(graphics);
Core.Console.Initialise(renderer); // Initialised a second time for graphics services.
Transform.ScreenSize = new Vector2(Backend.ScreenWidth, Backend.ScreenHeight);
Core.Audio.MediaPlayer.Initialise(new Audio.MediaHandler());
Core.Audio.SoundEffect.Initialise(Audio.SoundEffectXNA.SetVolume);
Core.Input.Input.Set(new KeyboardReaderXNA(), new MouseReaderXNA(), new GamePadReaderXNA(), new Input.Touch.TouchPanelXNA(), new GamePadOutputXNA());
Core.Input.InputManager.Initialise();
}
#endregion
#region Methods: Loading and Unloading Content
protected override void UnloadContent()
{
content.Unload();
base.UnloadContent();
}
#endregion
#region Methods: Drawing
protected override void EndDraw()
{
Camera2D.AllOff();
base.EndDraw();
}
#endregion
Graphics Backend Class
The graphics backend (XNA) class is a simple wrapper class. It extends the GraphicsBackend class, which is abstract and contains no functionality.
public class GraphicsBackendXNA : GraphicsBackend
{
#region Data: Fields
private Microsoft.Xna.Framework.GraphicsDeviceManager device;
#endregion
#region Data: Properties
public override Display Display { get { return device.GraphicsDevice.PresentationParameters.ToHeron(); } }
public override Core.Graphics.Viewport Viewport { get { return device.GraphicsDevice.Viewport.ToHeron(); } }
public override int ScreenWidth { get { return device.PreferredBackBufferWidth; } set { device.PreferredBackBufferWidth = value; } }
public override int ScreenHeight { get { return device.PreferredBackBufferHeight; } set { device.PreferredBackBufferHeight = value; } }
public override bool IsFullScreen { get { return device.IsFullScreen; } set { device.IsFullScreen = value; } }
public override Core.DisplayOrientation SupportedOrientations
{
get { return ((Core.DisplayOrientation)((int)device.SupportedOrientations); }
set { device.SupportedOrientations = (Microsoft.Xna.Framework.DisplayOrientation)((int)value); }
}
#endregion
#region Methods: Construction
public GraphicsBackendXNA(Microsoft.Xna.Framework.Game game)
{
device = new Microsoft.Xna.Framework.GraphicsDeviceManager(game);
}
#endregion
#region Methods: Settings
public override void ApplyChanges()
{
device.ApplyChanges();
}
#endregion
#region Methods: Rendering
public override void Clear(Colour colour)
{
device.GraphicsDevice.Clear(new Microsoft.Xna.Framework.Color(colour.R, colour.G, colour.B, colour.A));
}
#endregion
#region Methods: Renderer
public override Renderer2D CreateRenderer() { return new Renderer2DXNA(this); }
#endregion
Renderer 2D XNA Class
The Renderer 2D XNA class is another simple wrapper. It inherits from the Renderer2D base class, which implements a great number of abstract methods for drawing content. it has some very basic functionality which I will details first, but I won't bother including all the blank methods.
public class Renderer2D : IDisposable
{
private GraphicsBackend backend;
private Matrix currentMatrix;
public virtual GraphicsBackend Backend { get { return backend; } protected set { backend = value; } }
public Matrix CurrentMatrix { get { return currentMatrix; } set { currentMatrix = value; } }
public Renderer2D(GraphicsBackend backend) { this.backend = backend; CurrentMatrix = Matrix.Identity; }
}
public class Renderer2DXNA : Renderer2D
{
#region Static: 1 Pixel Texture
private static Texture2D filler;
private static bool Initialised { get { return filler != null; } }
public static void Initialise(GraphicsBackendXNA backend)
{
filler = CreateFiller(backend)
}
private static Texture2D CreateFiller(GraphicsBackendXNA backend)
{
Texture2D rt = new Texture2D(backend.Device.GraphicsDevice, 1, 1);
rt.SetData<Microsoft.Xna.Framework.Color>(new Microsoft.Xna.Framework.Color[] { Microsoft.Xna.Framework.Color.White });
return rt;
}
#endregion
#region Data: Fields
private SpriteBatch renderer;
#endregion
#region Methods: Construction
public Renderer2DXNA(GraphicsBackendXNA backend) : base(backend)
{
if (!Initialised)
Initialise(backend);
renderer = new SpriteBatch(backend.Device.GraphicsDevice);
}
#endregion
#region Methods: Begin and End
public override void Begin(Core.Graphics.SpriteSortMode sortMode, BlendMode blendMode, Core.Matrix transformMatrix)
{
renderer.Begin(); // Obviously not fully implemented, but simple for now.
}
public override void End() { renderer.End(); }
#endregion
#region Methods: Draw Image
public override void Draw(Image Image, Core.Rectangle Boundary, Core.Rectangle? Source, Colour DrawColour, float RotationRadians, Core.Vector2 Origin, Core.SpriteEffects Effects, float Layer)
{
if (!(Image is ImageXNA))
return;
ImageXNA realImage = Image as ImageXNA;
Microsoft.Xna.Framework.Rectangle finalSource =
Source.HasValue ? Source.Value.ToXNARectangle()
: new Microsoft.Xna.Framework.Rectangle(0, 0, realImage.Width, realImage.Height);
renderer.Draw(
realImage.Texture,
Boundary.ToXNARectangle(),
finalSource,
new Microsoft.Xna.Framework.Color(DrawColour.R, DrawColour.G, DrawColour.B, DrawColour.A),
RotationRadians,
Origin.ToXNAVector2(),
(SpriteEffects)((int)Effects),
Layer);
}
#endregion
}
// I'll leave off string and shape drawing, considering they're not being used here.
adding device.IsFullScreen = true; in the constructor fixed similar problem for me. I would see the pattern of diagonal lines whenever I tried to draw anything.
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 480;
graphics.PreferredBackBufferHeight = 800;
graphics.IsFullScreen = true;
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333);
// Extend battery life under lock.
InactiveSleepTime = TimeSpan.FromSeconds(1);
}
Here's how my glitch looked like

How to raise an action through an interactivity trigger at an unittest?

i got some little problems. I want to write some unittest fora c#/wpf/interactivty project with visual studio 2010. and dont forget im a beginner, so sorry for that ;)
the unittest should simulate a (virtual) Key Down Event on a textbox and the result should raise an action.(Action result: Console output - just to check as first step)
i still fixed 2 problems -> the dispatcher problem & the presentationSource bug.
The unittest still simulates the keyevent and the keyevent reached the textbox but the question is, why the action not raised through the keydown event on the textbox?
It's a threading problem? what's my missunderstand?
here is the code
The Unittest
at the end of the unittest u could check the textbox - the keyboard works
[TestMethod]
public void simpleTest()
{
var mockWindow = new MockWindow();
//simple test to check if the virtualKeyboard works
string CheckText = "Checktext";
mockWindow.SendToUIThread(mockWindow.textbox, CheckText);
mockWindow.SendToUIThread(mockWindow.textbox, "k");
//needed to start the dispatcher
DispatcherUtil.DoEvents();
}
the Dispatcher fix
public static class DispatcherUtil
{
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
private static object ExitFrame(object frame)
{
((DispatcherFrame)frame).Continue = false;
return null;
}
}
My Testaction
class TestAction : TriggerAction<UIElement>
{
protected override void Invoke(object parameter)
{
Console.WriteLine("testAction invoke");
}
}
The MockWindow
public class MockWindow : Window
{
public TextBox textbox { get; private set; }
public MockWindow()
{
//add a grid&textbox
Grid grid = new Grid();
textbox = new TextBox();
this.Content = grid;
grid.Children.Add(textbox);
//create the testaction/triggerEvent & add them
TestAction testAction = new TestAction();
System.Windows.Interactivity.EventTrigger TestTrigger = new System.Windows.Interactivity.EventTrigger();
TestTrigger.EventName = "KeyDown";
TestTrigger.Actions.Add(testAction);
TestTrigger.Attach(this.textbox);
}
//enter a keyboard press on an UIElement
public void SendToUIThread(UIElement element, string text)
{
element.Dispatcher.BeginInvoke(new Action(() =>
{
SendKeys.Send(element, text);
}), DispatcherPriority.Input);
}
}
the MockKeyboard added from codeplex sendkeys + a presentationCore fix for unittest(added at class SendKeys)
public class FixPresentationSource : PresentationSource
{
protected override CompositionTarget GetCompositionTargetCore()
{
return null;
}
public override Visual RootVisual { get; set; }
public override bool IsDisposed { get { return false; } }
}

Categories