📜 ⬆️ ⬇️

Creating ASP.NET AJAX control with the ability to communicate with the server

Hello, Habr!

I want to share my experience solving the problem, the answer to which I am difficult to find on the Internet (Russian-speaking - for sure).

I recently met with the fact that I was instructed to write ASP.NET AJAX control server side. This is such a control that is available when editing an ASP.NET page in a designer (toolbox), part of its logic is on the server, and part is on the client, the interaction occurs ayaksovo. The essence of my control is a simple ComboBox, which would receive data dynamically from the server (it is assumed that there is so much data that you can’t download them all at once), and also filter the list based on the characters entered in the text field. This is almost implemented in the AJAX Control Toolkit, only the list is loaded there entirely.
There are quite a few different articles and blogs on the Internet about creating their own controls, however, oddly enough, they are all limited to simple examples in which there is no description of the mechanism of interaction between the client and server. Most sources quote from standard microsoftov textbooks, where the ajax is that when you hover the mouse over the button, it changes its picture (det. Garden). I also needed the data from the list that was stored on the server to be added to the list on the client (it could just be a list, some ORM and in general everything that happens to be IEnumerable).
')
Just want to say that here you will not find a manual for creating your own control from scratch, the benefit of this part of the documentation is enough . I will describe how to make sure that the client part and its server half can freely exchange information in asynchronous mode.

Go to the point. In order for a client to asynchronously request data from a server, there are several ways in ASP.NET. The most convenient of them is interaction through a web-service. This approach allows you to transfer data back and forth in any convenient form for you, from the line to the full-fledged objects (including composite ones). ASP.NET does all the serialization for you. However, with all its attractiveness, such an approach could not be applied to my task, since it required control with all the “out of the box” functionality. That is, added it to the page, indicated the source of the data, and that’s all - he will do the rest for you. The Web service cannot be included in the component assembly, and accordingly, on subsequent pages that use it. To do this, programmers-users of my control would need to add an extra service, which they could well do without.

The second method is the implementation of the ICallbackEventHandler interface. This interface is used to indicate that the control implementing it can be the target of a client-side callback event (MSDN). This interface provides two methods: RaiseCallbackEvent , which accepts event data from the browser in a string parameter, and GetCallbackResult , which is responsible for returning the result back.
This approach was the most acceptable for me, since the functionality of ajax will always be under control, no matter where it is. I will give the code of the server and client sides to clarify the situation.

Server part:
  1. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  2. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  3. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  4. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  5. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  6. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  7. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  8. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  9. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  10. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  11. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  12. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  13. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  14. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  15. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  16. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  17. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  18. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  19. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  20. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion
  21. #region ICallbackEventHandler implementation public void RaiseCallbackEvent( string eventArg) { m_callbackEventArg = eventArg; } public string GetCallbackResult() { string result = "" ; if (DataSource != null ) foreach ( object o in DataSource) { string item = o.ToString(); if (item.StartsWith(m_callbackEventArg)) result += item + "||" ; } if (result.Length > 2) return result.Substring(0, result.Length - 2); else return "" ; } #endregion

The RaiseCallbackEvent method only remembers the string that came from the client. It stores the text that the user entered on the site in the input field of our control. This value will filter the results of the sample.
The GetCallbackResult method is endowed with a bit more logic. Here there is an element-by-step list traversal (stored in the DataSource), each line in it is compared with the filter value, and if all is well, it is entered into the result. Yes, it is worth noting that the disadvantage of this approach is that both the query and the result must be a string. That is, it is our concern how to serialize data. In my case, it was enough to separate the different strings with the substring ||, however, for more serious projects, we will have to be somewhat confused.

Now let's take a look at the client code:
  1. _populateListBegin: function () {
  2. if ( this .get_loadDynamically () == false ) {
  3. this ._filterList ( this ._textFilter);
  4. this ._displayList ();
  5. }
  6. else {
  7. WebForm_DoCallback ( this .get_element (). Id, this ._textFilter, this ._onPopulateListComplete, this .get_element (). Id, null , true );
  8. }
  9. },
  10. _onPopulateListComplete: function (result, context) {
  11. var rscb = $ find (context);
  12. rscb._processCallbackResult (result);
  13. }

This code is part of the prototype of a list object implemented in JavaScript on the client. The first function is called when the user clicks on the "expand" list (event registration and button code is omitted, as it relates to another topic). An interesting point for us is the call to the WebForm_DoCallback function, which starts the process of accessing the server side. As parameters, it takes the ID of the object that caused the callback request, the request parameter (string), the delegate to the response handler function, the context that can be used when processing the response, the delegate to the error handler function (we do not need it here simplicity control), and a boolean parameter, meaning asynchronous or not sending. Actually this is all the magic of ajax request.
The _onPopulateListComplete function will be called when a response is received from the server. It should be noted that it is not executed in the context of the object, so the call to this can give an incorrect result. That is why I passed in the context of the object id.

Here are all the little things that need to be done when implementing a server control with a full AJAX exchange. Do not hit hard, this is my first post. If there are any questions on the content (I am sure they will), I will answer in the comments with pleasure.

Source: https://habr.com/ru/post/83196/


All Articles