How to get the callback to viewmodel from library - c#

I have a UIControl(LatestHeadline) in a view(Home.xaml)(there are around 10 more controls on the same page) which is a textblock.I want to set the text of this control when user clicks on a button(named "Retrieve") from the same view.I have to call the method in utilities library from my viewmodel .The method(GetLatestHeadline) in the utilitieslibrary connects to a web api through webclient class and it fires the (deleagate)method Retrieve_Completed event.
Once this event is completed the required data is retrieved from eventargs paramater e.Now from the utilitieslibrary I want to return the data to the viewmodel so that the I can bind this to the LatestHeadline textblock.
LatestHeadline textblock has binding to a property(named "PropHeadLine") defined in viewmodel.
Is there a way we can achieve this in C#.Net?

If you have access to the utility class from the view model, then surely you can just add a handler to your Retrieve_Completed delegate in the view model.
utilityClass.Retrieve_Completed += UtilityClassRetrieve_Completed;
utilityClass.GetLatestHeadline();
...
public void UtilityClassRetrieve_Completed(EventArgs e)
{
// Do something with your e.New value here in the view model
LatestHeadlineProperty = e.New;
}
Of course, I'm just guessing at what your Retrieve_Completed definition is here, but hopefully you get the idea.

I like the concept of a "callback" for this kind of thing -- specifically, pass an Action<string> from the View Model to the service. The service can then save a reference to the callback, or pass it along to the completed-handler in the "UserState". So, the utilities method would look something like this:
public void GetLatestHeadline(Action<string> callback)
{
_apiClient.Retrieve(userState: callback);
}
private void Retrieve_Completed(object sender, RetrieveCompletedEventArgs args)
{
var callback = args.UserState as Action<string>;
if (callback != null)
callback(args.Result.Headline);
}
Now the view model can pass a callback to the utilities method -- the callback should presumably just set the property "PropHeadLine" to the result value:
private void Button_Click()
{
Utilities.GetLatestHeadline( result => PropHeadLine = result );
}

Expose a Delegate or Event in your service, in your view model just subscribe or hook to
that delegate or event
when ever you want to notify the viewmodel Once a particular operation is completed, just invoke the delegate or event
The method which is hooked in your viewmodel will get called.
In your viewmodel now you can do the necessary actions you want

Related

ObservableCollection not changing in MVVM when using NServiceBus

I have a strange behavior of the ObservableCollection in my MVVM app, more exactly in the part of the code responsible for receiving the messages from NServiceBus:
public class MyViewModel: ViewModelBase, IHandleMessages<CarMoved>
{
public ObservableCollection<CarData> Cars= new ObservableCollection<CarData>();
public Task Handle(CarMoved message, IMessageHandlerContext context)
{
...
Cars.Add(new Car());
return Task.CompletedTask;
}
}
So I expect the Handle method to add a new object into my ObservableCollection but the number of the objects remains the same.
I created the test button to check whether I can add a new object using a button and this testing button works fine.
I also debugged the Handle method and I can see that the number of the objects gets increased in the Observable collection while I am in the Handle method, but all that changes once I leave the method- the number of the objects returns to the old number.
I tried to add the object using Task.Run(..); Task.Wait(); presuming that maybe it needs some time. It did not help.
Please advise how I could resolve this issue and why it happens?
Ok, so the problem here is that NServiceBus will create a new instance of your ViewModel to handle the message. This is obviously not what you want.
Instead the ViewModel and message Handler should be separate objects. Then the handlers can tell the ViewModel about the message.
I'm not sure what MVVM framework you're using, but usually there's some sort of event aggregator for passing messages from system components like NServiceBus handlers and ViewModels.
Make sure that you access the ObservableCollection on the UI thread using the dispatcher:
public Task Handle(CarMoved message, IMessageHandlerContext context)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => { Cars.Add(new Car()); }));
return Task.CompletedTask;
}
Alternatively you could try to use the BindingOperations.EnableCollectionSynchronization method to enable the collection to be accessed across multiple threads:
public class MyViewModel : ViewModelBase, IHandleMessages<CarMoved>
{
private readonly object _lock = new object();
public ObservableCollection<CarData> Cars = new ObservableCollection<CarData>();
public MyViewModel()
{
Application.Current.Dispatcher.Invoke(() => BindingOperations.EnableCollectionSynchronization(Cars, _lock));
}
public Task Handle(CarMoved message, IMessageHandlerContext context)
{
Cars.Add(new Car());
return Task.CompletedTask;
}
}
If you still cannot make you should read this: https://stackoverflow.com/help/mcve

Removing event handlers in c#

I'm working on a Windows Forms app and I've come to a point where I can't understand what's happening.
I have something similar to an MVC architecture. Sometimes I want controls that belong to the view to stop listening to events. So inside the view code I've written a method that looks like this:
public void enableEventHandlers(bool enable)
{
if (enable)
{
control.someEvent += someEventHandler;
}
else
{
control.someEvent -= someEventHandler;
}
}
The thing is: when I want to remove an event handler I just call this method with false as a parameter.
If I call this method from inside the view code it works just fine. But if I call this method from inside the controller code, it doesn't work (the event handlers are not removed).
Just to give a little more context:
This works:
public partial class View : Form
{
public void enableEventHandlers(bool enable)
{
// The code I already showed
}
public void doSomething()
{
enableEventHandlers(false);
// do something
enableEventHandlers(true);
}
}
This doens't work:
public class controller
{
private View myView;
public void doSomething()
{
myView.enableEventHandlers(false);
// Do something... but somehow everything inside my Form is still paying attention to events
myView.enableEventHandlers(true);
}
}
Finally I found the problem. It seems that somehow I was attaching an event handler twice to the same Control. I couldn't find the exact line number where I was doing that anyway. The solution I found is to remove an event handler before adding a new one.
So the method enableEventHandlers looks now like this:
public void enableEventHandlers(bool enable) {
if (enable)
{
control.someEvent -= someEventHandler;
control.someEvent += someEventHandler;
}
else
{
control.someEvent -= someEventHandler;
}
}
Thanks for your answers.
I don't know if that's it but you didn't initialize your View. You just say "private View view", but that doesn't point to anywhere. You want to either make a new View by doing private View v = new View(), or let that view point to the view that you want to change the events.

How to Update View from ViewModel using MVVMCross and Xamarin.Android

Am new to MVVMCross with xamarin.android so little struck up with a scenario. I have a fab and mvx.recyclerview inside a fragment. so when i click on this fab it will make the recyclerview scroll by a row.
i.e
void onclick(sender object ,eventargs e)
{
mrecyclerview.SmoothScrollToPosition(somevariable++); // do something.
}
this is breaking mvvm pattern, so is there any way or method in the MVVM Cross which i can use to listen back to the View from ViewModel.
fab.click binding with ICommand => viewmodel => view=> updatescroll().
thanks in advance.
Well, since the ViewModel should not know about the View, you should not call any method of it.
I would propose an event in your ViewModel where your View can subscribe. So you call your event something like FabClickDone and your view does what ever it wants, when this event occured. In your case scrolling.
Here is an code example for your ViewModel:
public delegate void FabClickDoneEvent(object sender, EventArgs args);
public event FabClickDoneEvent FabClickDone;
protected virtual void OnFabClickDone()
{
FabClickDone?.Invoke(this, EventArgs.Empty);
}
You then just call it by
void onclick(sender object , eventargs e)
{
// Do something inside your viewmodel
// ...
OnFabClickDone();
}
In your View constructor subscribe to this event:
ViewModel.FabClickDone += ViewModel_FabClickDone;
And create a method where you want to do your scrolling
void ViewModel_FabClickDone(Object sender, EventArgs e)
{
mrecyclerview.SmoothScrollToPosition(somevariable++); // do something.
}
Since you're using MVVMcross I would suggest you using a command, where you call OnFabClickDone();

Event handling in a disconnected class

I am looking for a way to handle an event raised by class A in class B without coupling the classes.
The situation is that class A is a state machine. I need to send an email when a state of class A becomes Active. I would like to create a class that would listen up for that event and handle it.
I encapsulate the state logic in class A and I instantiate it from a web page (asp.net webform).
I could instantiate the email class inside the webform as well, but I do not wish to do so, in that case, the developer that codes the webform will have to hook up the logic and events. I would like to push all that logic back to the business layer. But I can't seem to have a way to do it.
in BLL
class StateHandler()
{
public event EventHandler StateChanged;
SetState(int newState)
{
// to keep things simple for sample let's assume 2 states
if (newState ==1 ) this.State = "Active";
else this.State = "Inactive";
if (this.StateChanged != null)
this.StateChanged(this, new EventArgs());
}
}
class EmailProvider()
{
SendEmail()
{
// logic to send email
}
}
Asp.net page
....
void OnSubmit_click()
{
StateHandler sh = new StateHandler();
//adds an event handler to the StateChanged event
sh.StateChanged += new EventHandler(sh_StateChanged);
//setting this will fire the StateChanged event
sh.SetState(1);
}
EventBus might be a useful pattern for your case. It is simply a message broker that passes events to registered subscribers. I don't know if there is any C# library for that. But is should be easily implemented. Have a look at guava's EventBus. It's Java but you shoud get the idea.
In a container managed situation like EJB the container handles asynchronous message dispatch. In Java/Java EE again you'd have Message Driven Beans.

How to get what event raise in Http Module

I want to get what event in what class cause request from server in Http Module.
I mean foe example : when user clicks on a button in Page1 I want to get : Button1_Click in Page1 class or when user changes a dropdown list selected index in that page I want to get DropdownList1_SelectedIndexChange in Page1 class.
thanks
Page events are associated with the page. modules are lifecycle events. You won't see any click type event from events modules listen to like these from another post
BeginRequest
AuthenticateRequest
AuthorizeRequest
ResolveRequestCache
AcquireRequestState
PreRequestHandlerExecute
PostRequestHandlerExecute
ReleaseRequestState
UpdateRequestCache
EndRequest
HTTPModule Event Execution Order?
Your question is very wide and the following MSDN Library documentation references might help you to understand this process:
ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
ASP.NET Application Life Cycle Overview for IIS 7.0
Here are the events and the request pipeline for ASP.NET 4.0:
Validate the request, which examines the information sent by the browser and determines whether it contains potentially malicious markup.
Perform URL mapping, if any URLs have been configured in the UrlMappingsSection section of the Web.config file.
Raise the BeginRequest event.
Raise the AuthenticateRequest event.
Raise the PostAuthenticateRequest event.
Raise the AuthorizeRequest event.
Raise the PostAuthorizeRequest event.
Raise the ResolveRequestCache event.
Raise the PostResolveRequestCache event.
[IIS 5.0/6.0] Based on the file name extension of the requested resource (mapped in the application's configuration file), select a class that implements IHttpHandler to process the request. If the request is for an object (page) derived from the Page class and the page needs to be compiled, ASP.NET compiles the page before creating an instance of it. [IIS 7.0] Raise the MapRequestHandler event. An appropriate handler is selected based on the file name extension of the requested resource. The handler can be a native-code module such as the IIS 7.0 StaticFileModule or a managed-code module such as the PageHandlerFactory class (which handles .aspx files).
Raise the PostMapRequestHandler event.
Raise the AcquireRequestState event.
Raise the PostAcquireRequestState event.
Raise the PreRequestHandlerExecute event.
Call the ProcessRequest method (or the asynchronous version IHttpAsyncHandler.BeginProcessRequest) of the appropriate IHttpHandler class for the request. For example, if the request is for a page, the current page instance handles the request.
Raise the PostRequestHandlerExecute event.
Raise the ReleaseRequestState event.
Raise the PostReleaseRequestState event.
Perform response filtering if the Filter property is defined.
Raise the UpdateRequestCache event.
Raise the PostUpdateRequestCache event.
[IIS 7.0] Raise the LogRequest event.
[IIS 7.0] Raise the PostLogRequest event.
Raise the EndRequest event.
Raise the PreSendRequestHeaders event.
Raise the PreSendRequestContent event.
Note: The MapRequestHandler, LogRequest, and PostLogRequest events are supported only if the application is running in Integrated mode in IIS 7.0 and with the .NET Framework 3.0 or later.
References to follow: HTTP Handlers and HTTP Modules Overview
The events that you are looking for are specific to the asp.net page model. Http module is at a lower level ( transport based ) and would not be used to capture page events.
Can you provide more detail please?
I'm going to suggest that if you are starting with a website to inherit a class from page and make all your pages to inherit from this class.
public abstract class LoggingPage : System.Web.UI.Page
{
protected override void RaisePostBackEvent(
IPostBackEventHandler sourceControl, string eventArgument)
{
//doing something with the information.
EventLog.WriteEntry("Page event for " + sourceControl.UniqueID + " at " + this.Request.Url);
//then call the base implementation
base.RaisePostBackEvent(sourceControl, eventArgument);
}
}
If you need to get the information of the event because of exceptions and you never get to RaisePostBackEvent, then you will need to handle PreRequestHandlerExecute of HttpApplication in your module and get 2 fields in the request
public class LoggingModule : System.Web.IHttpModule
{
private HttpApplication _app;
public void Dispose()
{
this._app.PreRequestHandlerExecute -= new EventHandler(this.PreRequestExecution);
}
public void Init(HttpApplication application)
{
this._app = application;
this._app.PreRequestHandlerExecute += new EventHandler(this.PreRequestExecution);
}
private void PreRequestExecution(object sender, EventArgs e)
{
var request = this._app.Context.Request;
var target = request.Form["__EVENTTARGET"];
var arg = request.Form["__EVENTARGUMENT"];
//this gives you enough information about events
//you need to check if they are null before using them (target and arg)
//through the same request you can get extra info including URL
}
}
UPDATE:
If your concern is security and since you have roles implemented in your system I would recommend to make use of System.Security.Permissions.PrincipalPermissionAttribute to decorate your event handlers like this:
protected void Page_Load()
{
myButton.Click += new EventHandler(this.myButton_Click);
}
[PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]
private void myButton_Click(object sender, EventArgs e)
{
//your code to handle the event
}
You can add the attributes multiple times to fit your needs.
hope this helps.
Regarding your question I noticed the remark:
I want to develop a security system that add some attributes to event (for roles that can access to this event) and check authorization using session and this attributes.I want to get event name and then attributes that belong to it and check authorization
Because events are registered in the class, which is not available during the Module/Handler phase, what you ask cannot be done.
However, there are always options, and I can see what you're trying to achieve :-) My solution is to simply register all events by calling a method (myEventHooks.HookAll(this);) and in the hook implementation check security (and either throwing an exception or removing all registered events when the check fails -- fill in the gaps however you see fit).
Note that when you change the Children / tree, you need to update the event hooks to bind to all their methods as well. The easiest way to do the binding is by overriding RaisePostBackEvent in a base page and then hooking everything.
This solution can be improved in a couple of different ways; most obviously making the handling more generic as well and lack of the 0-parameter handler. I kept it as simple as possible for clearance. This should get you started.
My solution has 2 parts: (1) a generic hook class and (2) the implementation in the form. Currently the solution is lazy, e.g. I put event handlers at the end, not at the front of the queue. You should be able to fix this by using GetInvocationList or something similar.
The generic hook class basically hooks the events and fires when an event is called:
public class EventHooks
{
private class EventHooksEquality : IEqualityComparer<Tuple<string, object>>
{
public bool Equals(Tuple<string, object> x, Tuple<string, object> y)
{
return x.Item1.Equals(y.Item1) && object.ReferenceEquals(x.Item2, y.Item2);
}
public int GetHashCode(Tuple<string, object> obj)
{
return obj.Item1.GetHashCode();
}
}
public void CheckSecurity(string eventName, object container)
{
// Add your security code that checks attributes and the likes here
}
private abstract class BaseHookHandler
{
protected BaseHookHandler(object container, string eventName, EventHooks hooks)
{
this.hooks = hooks;
this.container = container;
this.eventName = eventName;
}
protected string eventName;
protected object container;
protected EventHooks hooks;
}
private class HookHandler<T1> : BaseHookHandler
{
public HookHandler(object container, string eventName, EventHooks hooks)
: base(container, eventName, hooks)
{
}
public void Handle(T1 t1)
{
hooks.CheckSecurity(eventName, container);
}
}
private class HookHandler<T1, T2> : BaseHookHandler
{
public HookHandler(object container, string eventName, EventHooks hooks)
: base(container, eventName, hooks)
{
}
public void Handle(T1 t1, T2 t2)
{
hooks.CheckSecurity(eventName, container);
}
}
// add more handlers here...
public void HookAll(object obj)
{
foreach (var eventHandler in obj.GetType().GetEvents())
{
Hook(obj, eventHandler.Name);
}
}
public void Hook(object obj, string eventHandler)
{
if (obj == null)
{
throw new Exception("You have to initialize the object before hooking events.");
}
// Create a handler with the right signature
var field = obj.GetType().GetEvent(eventHandler);
var delegateInvoke = field.EventHandlerType.GetMethod("Invoke");
Type[] parameterTypes = delegateInvoke.GetParameters().Select((a) => (a.ParameterType)).ToArray();
// Select the handler with the correct number of parameters
var genericHandler = Type.GetType(GetType().FullName + "+HookHandler`" + parameterTypes.Length);
var handlerType = genericHandler.MakeGenericType(parameterTypes);
var handlerObject = Activator.CreateInstance(handlerType, obj, eventHandler, this);
var handler = handlerType.GetMethod("Handle");
// Create a delegate
var del = Delegate.CreateDelegate(field.EventHandlerType, handlerObject, handler);
// Add the handler to the event itself
field.AddEventHandler(obj, del);
}
}
Usage in the base class can be done as follows (example):
protected override void RaisePostBackEvent(
IPostBackEventHandler sourceControl, string eventArgument)
{
// Hook everything in Page.Controls
Stack<Control> st = new Stack<Control>();
st.Push(Page);
while (st.Count > 0)
{
var control = st.Pop();
eventHooks.HookAll(control);
foreach (Control child in control.Controls)
{
st.Push(child);
}
}
// Raise events
base.RaisePostBackEvent(sourceControl, eventArgument);
}
private EventHooks hooks = new EventHooks();

Categories

Resources