This is quite straight forward(ish) to do is the event is 'real' as in now created by DynamicProxy, but I can't work anything out for a mocked event.
The best way to explain what I'm trying to achieve is with code, please see the comment lines in the test method:
using System;
using Moq;
using NUnit.Framework;
namespace MOQTest
{
[TestFixture]
public class EventsMoqTest
{
[Test]
public void DetachTest()
{
var hasEventMock = new Mock<IHasEvent>();
using (var observer = new Observer(hasEventMock.Object))
{
//Assert that hasEventMock.Object has handler attached
}
//Assert that hasEventMock.Object DOES NOT have handler attached
}
}
public interface IHasEvent
{
event EventHandler AnEvent;
}
public class Observer : IDisposable
{
private readonly IHasEvent _hasEvent;
private readonly EventHandler _hasEventOnAnEvent;
public Observer(IHasEvent hasEvent)
{
_hasEvent = hasEvent;
_hasEventOnAnEvent = _hasEvent_AnEvent;
_hasEvent.AnEvent += _hasEventOnAnEvent;
}
void _hasEvent_AnEvent(object sender, EventArgs e)
{}
public void Dispose()
{
_hasEvent.AnEvent -= _hasEventOnAnEvent;
}
}
}
Unfortunately, you can't. This isn't really a moq issue, but the way the C# event keyword works with delegates. See this SO answer for more information.
Related
Using a custom renderer one can disable the swiping gesture of an CarouselPage on iOS like so:
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(CarouselPage), typeof(CustomCarouselPageRenderer))]
namespace App.iOS
{
public class CustomCarouselPageRenderer : CarouselPageRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
UIView view = this.NativeView;
UIScrollView scrollView = (UIKit.UIScrollView)view.Subviews[0];
scrollView.ScrollEnabled = false;
}
}
}
How to accomplish the same on Android?
using Android.Content;
using XamFormsApp.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CarouselPage), typeof(CustomCarouselPageRenderer))]
namespace StixApp.Droid.Renderers
{
public class CustomCarouselPageRenderer : VisualElementRenderer<CarouselPage>
{
public CustomCarouselPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
{
base.OnElementChanged(e);
var view = this.RootView;
X
X
}
}
}
There appears to be no way to access Subviews in the same way. One can access Children like so
Android.Views.View view = (Android.Views.View)GetChildAt(i);
How to know which Child is ScrollView if any?
Using a loop to check for this, like so,
for (int i = 0; i < ChildCount; ++i)
{
Android.Views.View view = (Android.Views.View)GetChildAt(i);
if (view is ScrollView)
{
}
}
Yields the following: "The given expression is never of the provided (ScrollView) type"
So! How to disable CarouselPage swipe/scrolling as is done in iOS quite elegantly?
UPDATE: Please see sample solution.
A couple of things.
For Android the view you are looking for is not a ScrollView but a ViewPager.
This can be found under the index 0 with the GetChildAt method.
Also, why are you using VisualElementRenderer<CarouselPage> as the parent class of your CustomCarouselPageRenderer. Instead use the CarouselPageRenderer as you did with iOS.
One last thing is that on Android the Scroll of a ViewPager cannot be disabled. To get this behavior you can listen to the Touch event. Setting the Handled property of TouchEventArgs to true will prevent the scrolling from happening.
Your whole class would look something like:
[assembly: ExportRenderer(typeof(CarouselPage), typeof(CustomCarouselPageRenderer))]
namespace StixApp.Droid.Renderers
{
public class CustomCarouselPageRenderer : CarouselPageRenderer
{
private bool _canScroll = false;
public CustomCarouselPageRenderer(Context context) : base(context)
{
}
public CustomCarouselPageRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
{
base.OnElementChanged(e);
if (this.ChildCount > 0 && this.GetChildAt(0) is ViewPager viewPager)
{
viewPager.Touch -= ViewPagerTouched;
viewPager.Touch += ViewPagerTouched;
}
}
private void ViewPagerTouched(object sender, TouchEventArgs e)
{
e.Handled = !_canScroll;
}
}
}
Just change the value of _canScroll to true to allow the scrolling.
Hope this helps.-
Overridden Methods in ViewPager class:
public class NonSwipeableViewPager : ViewPager
{
public override bool OnTouchEvent(MotionEvent e)
{
return false;
}
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
return false;
}
public override bool ExecuteKeyEvent(KeyEvent ev)
{
return false;
}
}
Changes to CarouselPageRenderer:
In class declaration:
public class MyCarouselPageRenderer : VisualElementRenderer<CarouselPage>
{
NonSwipeableViewPager _viewPager;
}
In OnElementChanged:
protected override void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
{
_viewPager = new NonSwipeableViewPager(Context);
}
Note: CarouselPageAdapter, CarouselPageRenderer, MeasureSpecFactory, ObjectJavaBox, and PageContainer all had to be copied from the Xamarin.Forms github to enable a custom CarouselPageRenderer implementation. All of this is in the github sample but hopefully this spells it out more clearly for future visitors.
Note2: I would like to stress that the true behavior we were trying to achieve is probably best done with a NavigationPage as this allows us to easily pop and push any and all pages at any time without having to address the swipe issue. That being said, hopefully this solution serves to aid anyone who need this behavior on a CarouselPage.
Ok, so, I'm trying to link an ObservableCollection from my Android project to my Cross-Platform Project::
I've got this so far...this is in my Cross-platform app
ObservableCollection<String> NewRef = DependencyService.Get<ISlateBluetoothItems>().test().testThing;
NewRef.CollectionChanged += TestThing_CollectionChanged;
listView.ItemsSource = NewRef;
private void TestThing_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
listView.ItemsSource = DependencyService.Get<ISlateBluetoothItems>().test().testThing;
Console.WriteLine("working");
}
The line "working" is never printed even if I make changes to the ObservableCollection on the android portion of my app...
Here's the interface I'm using for the DependencyService:
using System.Collections.ObjectModel;
namespace ThoughtCastRewrite.BluetoothX
{
public interface ISlateBluetoothItems
{
BluetoothItems test();
}
}
Here's the class I use to expose the list:
namespace ThoughtCastRewrite.BluetoothX
{
public class BluetoothItems
{
public ObservableCollection<String> testThing;
public BluetoothItems()
{
testThing = new ObservableCollection<String>();
testThing.Add("wtf?");
}
public void AddThis()
{
testThing.Add("ok");
}
}
}
This is in the Android portion of my app, it implements the ISlateBluetoothItems interface
BluetoothItems bluetoothItems = new BluetoothItems();
then I call
bluetoothItems.AddThis();
but "ok" is not added to my list! I don't get the CollectionChanged event firing off! What's the deal guys? What's the deal?
You should assign your ObservableCollection as a source of your listview only once, not after each change. Changes to the collection will be automaticcly propagated to the listview.
I am trying to get a function to be called everytime an event occurs. In the KinectRegion class there is an event called HandPointerGrip: http://msdn.microsoft.com/en-us/library/microsoft.kinect.toolkit.controls.kinectregion.handpointergrip.aspx.
I see that it has declared the event and it seems to me that the event has already been set to be invoked(HandPointerEventArgs)? How do I attach a function to this event?
public Menu()
{
KinectRegion.HandPointerGripEvent+=Hand_Gripped; // why doesn't this work? :(
}
private void Hand_Gripped(object sender, HandPointerEvnetArgs e)
{
MessageBox.Show("I work!"); // I wish this would work
}
Been working hard on this problem and here is something I think will work. Afraid to test it. Learning a lot about routed events, delegates, and events.
namespace ...
{
public delegate void HandPointerEventHandler(object sender, HandPointerEventArgs e);
public partial class thePage : Page
{
public event HandPointerEventHandler HandGripped
{
add {this.AddHandler(KinectRegion.HandPointerGripEvent,value);}
remove {this.RemoveHandler(KinectRegion.HandPointerGripEvent,vlaue);}
}
public thePage()
{
InitializeComponent();
this.HandGripped += new HandPointerEventHandler(OnHandGripped);
}
protected virtual void OnHandGripped(object sender, HandPointerEventArgs e)
{
MessageBox.Show("hello"); //hopefully
}
}
}
The first block of code should work fine. My guess is that the HandPointerGripEvent is hooked up ok, it just never fires.
How are you setting up your KinectRegion?
Are you updating the interration library each frame?
Perhaps this helps?
Kinect SDK 1.7: Mapping Joint/Cursor Coordinates to screen Resolution
KinectRegion.AddHandPointerGripHandler(this.Button1, this.Button1_Click);
Here Button1 is:
< k:KinectTileButton x:Name="Button1" Height="150" Width="150" Content="Click"/ >
The namespaces:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:k="http://schemas.microsoft.com/kinect/2013"
Button1_Click is the method itself, for example:
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("YOU GOT ME !!!");
}
If you want to add a grip handler for another interface object, you just do:
KinectRegion.AddHandPointerGripHandler(< object name >, < method name >);
And s.o.
We have a custom TraceListener implementation which only logs when a specific object (LogMessage) is received. This all works well when using directly with the Trace.Write(object) method.
Due to Performance reason, I want to separate the Listener, so all non-relevant Trace messages are not passed to the listener. Therefore I created a specific TraceSource whith only this listener attached.
Now I struggle to pass my custom log object (LogMessage) to the listener using the TraceSource. The TraceSource.TraceData(TraceEventType, int, object) always invokes the TraceListener.Write(string) method, not the TraceListener.Write(object) method.
Is there any way I can pass the custom object to the Listener using the TraceSource?
Sample code:
using System.Diagnostics;
namespace Sample
{
public class LogMessage
{
public byte[] Data { get; set; }
//...
}
public class Sample
{
public void Foo()
{
var ts = new TraceSource("Test");
var lm = new LogMessage();
//lm.Data = ...;
//this works: calls the Write(object) method in listener
Trace.Write(lm);
//this doesn't work: calls the Write(string) method in listener
ts.TraceData(TraceEventType.Information, 0, lm);
}
}
public class MyListener : TraceListener
{
public override void Write(string message)
{
//not in use
}
public override void WriteLine(string message)
{
//not in use
}
public sealed override void Write(object o)
{
if (o is LogMessage)
{
//do someting with the LogMessage
}
}
}
}
Thanks
Thomas
maybe it's too late for an answer but anyway :
By using a tool like JustDecompile you can easily see that TraceSource.TraceData uses TraceListener.TraceData method which itself basically calls WriteLine with object.ToString() for message.
So you'll have to override the ToString method for your class LogMessage in order to do as you want.
Say I have the controller as follows:
public class Controller
{
ISomeService _service;
public Controller(ISomeService service)
{
_service = service;
_service.EventFired += EventFired;
_service.SomeEventFired += SomeOtherEventFired;
}
private void EventFire(object sender, EventArgs e)
{
// Might occur on FireSomeEvents();
// Go to another controller
}
private void SomeOtherEventFired(object sender, EventArgs e)
{
// Might occur on FireSomeEvents();
// Go to another view on this page
}
public void Create()
{
_service.FireSomeEvents();
if(EventFired == true)
{
return View("EventFired");
}
else
{
return RedirectToAction("SomeOtherEventFired");
}
}
}
I want to be able to handle the Redirect and View in a better way, because in the end I will end up with 3 potential events on my service.
I'm just wondering whether this is a design smell, or whether there is a better way to implement the redirect to pages...
Events don't play nicely with ASP.NET MVC. Their model is not adapted to the MVC pattern. If you want to use them you may take a look at asynchronous controllers.