I have used the SP.SOD.executeOrDelayUntilScriptLoaded(func, depScriptFileName) quite a bit in my JSOM (https://msdn.microsoft.com/en-us/library/ff411788(v=office.14).aspx). Has anybody out there used SP.SOD.executeOrDelayUntilEventNotified(func, eventName) (https://msdn.microsoft.com/en-us/library/ff410354(v=office.14).aspx) successfully? For eventName is it as something as simple as "click"? I have searched the web but haven't found anything useful. Any Feedback appreciated.
Basically the difference between those functions that in first case the name of the file from client library is specified, for example sp.js (parameter depScriptFileName). In the latter case the event name should be specified, for example "sp.scriptloaded-sp.js" ( parameter eventName)
Here is the declaration for SP.SOD.executeOrDelayUntilEventNotified(func, eventName) from SharePoint Client library init.js:
function ExecuteOrDelayUntilScriptLoaded(func, depScriptFileName) {
depScriptFileName = depScriptFileName.toLowerCase();
var eventName = "sp.scriptloaded-" + depScriptFileName;
return ExecuteOrDelayUntilEventNotified(func, eventName);
}
About event names
The list of event names is stored in global variable called g_ExecuteOrWaitJobs. For every SharePoint Client library file is used a predefined event name, for example for a file sp.clienttemplates.js the corresponding event name is sp.scriptloaded-clienttemplates.js
Lets demonstrate how to utilize both SP.SOD.executeOrDelayUntilScriptLoaded(func, depScriptFileName) and SP.SOD.executeOrDelayUntilEventNotified(func, eventName) functions.
For that purposes let's introduce a simple example that prints SP.Web Title property:
function printWebInfo(){
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
ctx.load(web,'Title');
ctx.executeQueryAsync(
function(){
console.log(web.get_title());
},
function(sender,args){
console.log(args.get_message());
});
}
In the following example
ExecuteOrDelayUntilScriptLoaded(printWebInfo, "sp.js");
printWebInfo function will be invoked once SharePoint Client library sp.js is loaded.
The same example that utilizes SP.SOD.executeOrDelayUntilEventNotified(func, eventName) will look like this:
var eventName = "sp.scriptloaded-sp.js";
ExecuteOrDelayUntilEventNotified(printWebInfo,eventName);
where "sp.scriptloaded-sp.js" event name is used to determine whether sp.js library is loaded or not.
Related
I just learned about delegates and the publisher/subscriber pattern, however I have been having some problem implementing them in my current code, mainly because Im not sure what should be assign to what(I shall explain this).
I have a class, example Class A. It is a library class that contains codes that write logs into .txt file. I would like to be able to take these logs and write them somewhere else, example another .txt file/TextBox/RichTextBox.
Class A
//Just a library class for log functions
//Declare and instantiate the delegate
public void delegate myDel(string message)
public myDel customDel, customDel2
LogCategory(string category)
{
//Bunch of codes that separates the log into category Info/Warn/Error
WriteLog()
}
WriteLog()
{
StreamWriter sw = new StreamWriter(LogFilePath)
//writes logs into .txt file1
}
then in a separate class
Class B
//This is the main program where all the logs are written
public void PrintLog(string message)
{
Class A ca = new Class A();
ca.LogCategory();
}
public void delegateTheLogs()
{
//how do I use customDel to write the logs to another text file in a
//different directory
}
The idea is that delegate is suppose to:
act as a pointer
allow the program to write logs to multiple destination at the same time
The question is what do I use customDel for and how do I use it catch the logs and write them somewhere?
I think this is an interesting topic, and if anyone knows how to do this, please help me figure this out.
Oh and Im not interested in using events, I know delegate and events are pretty common to use together.
Thanks
Following on from my comment, here's an example. We have a class called FlexibleLogger that basically knows how to format stuff that it is given but it doesn't have any baked in ability to write the log data to anywhere, the idea being that the code that creates the logger also creates the routine that the logger will use to output:
public class FlexibleLogger{
Action<string> _logWriterAction;
public FlexibleLogger(Action<string> logWriterAction){
_logWriterAction = logWriterAction;
}
public Log(string message){
_logWriterAction($"{DateTime.UtcNow}: {Message}");
}
public Log(Exception ex){
Log(ex.Message);
}
}
This class doesn't know how to write a file, or console, or post the message to a web service, or email it, or put it in a rabbit queue etc.. all it knows how to do is formulate a log message provided into having a time at the start, or pull the message out of an exception(and then pass it to the method that puts a time at the start), and then call the Action (a neater way of declaring a delegate that takes arguments of various types and returns no value) passing in the message
The Action is some variable(able to be varied) method created by you
We might use it like this:
public class Program
{
public static void Main()
{
//it's a "local function", IMHO a neater way of providing a method that can be passed as an action
void consoleWriterFunc(string f){
Console.WriteLine(f);
};
//see the thing we pass as the Action parameter is a method/function,
//not a data item like a string, int, Person etc
var logger = new FlexibleLogger(consoleWriterFunc);
//log will make a string like "12-Dec-2020 12:34:56: a"
//and invoke the consoleWriterFunc, passing the string into it
//in turn it prints to the console
logger.Log("a");
//how about a logger that writes a file?
void fileWriterFunc(string f){
File.AppendAllText("c:\\temp\\some.log", f);
};
logger = new FlexibleLogger(fileWriterFunc);
logger.Log(new Exception("something bad happened"));
}
}
Doesn't have to be a local function, you can pass any method at all that takes a string and returns a void, as your Action<string>. It doesn't even have to be a method you wrote:
var sw = new System.IO.StringWriter();
var logger = new FlexibleLogger(sw.Write);
logger.Log("I'm now in the string writer" );
Microsoft wrote the method StringWriter.Write- it takes a strong, returns a void and calling logger.Log having passed the Write method of that stribgwriter instance means that the logger will Log into the stringwriter (a wrapper around a stringbuilder)
Hopefully this helps you understand that a delegate is "just a way to make a method into something you can pass as a parameter, just like anything else. They've been available for years, if you think about it, manifested as events. Microsoft have no idea what you want to do when you click a button, so they just have the button expose an event, which is really just a collection of delegates; a List of methods that the button should call when it's clicked.
I have a GeckoFX 29 GeckoWebBrowser in my C# application.
I navigate successfully to a webpage and I have an element that doesn't accept a simple "click()" and so I have to forcefully dispatch events.
The problem is that when I try to create the event the GeckoFX core throws an exception.
var eventName = "mouseover";
var domEvent = browser.DomDocument.CreateEvent(eventName);
domEvent.DomEvent.InitEvent(new nsAString(eventName), true, true);
The exception is thrown at CreateEvent(eventName) and the exception is this:
System.Runtime.InteropServices.COMException (0x80530009): Exception from HRESULT : 0x80530009
at Gecko.nsIDOMDocument.CreateEvent(nsAStringBase eventType)
at Gecko.nsString.GenericPass[T,TString](Func`2 func, String value) in c:\Users\micro_000\Documents\Visual Studio 2008\Projects\geckofx-29.0\Geckofx-Core\nsString.cs:line 221
at Gecko.nsString.Pass[T](Func`2 func, String value) in c:\Users\micro_000\Documents\Visual Studio 2008\Projects\geckofx-29.0\Geckofx-Core\nsString.cs:line 476
at Gecko.GeckoDomDocument.CreateEvent(String name) in c:\Users\micro_000\Documents\Visual Studio 2008\Projects\geckofx-29.0\Geckofx-Core\DOM\GeckoDomDocument.cs:line 155
at MyApp.Window.createEvent(GeckoDocument doc, String eventName)
I tried to look into the source code, but nsIDOMDocument is just an interface and there is no other information available for this issue that I can find.
It turns out that CreateEvent accepts only a few strings.
Because it didn't work I was trying to dispatch the events by calling a java script that I was going to insert in the document. Researching that I noticed that CreateEvent was used with the parameter "Events" and then you would init your event with the actual event name.
So far I found out that params that work with CreateEvent are "Events", "MouseEvent", "KeyboardEvent".
This code works:
var eventName = "mouseover";
var domEvent = browser.DomDocument.CreateEvent("MouseEvent");
domEvent.DomEvent.InitEvent(new nsAString(eventName), true, true);
I'm using Tridion 2011's Event System to perform some additional actions when un-publishing components. I'm using code found here to publish a related component.
I'm registering my event handler as follows:
EventSystem.Subscribe<Component, UnPublishEventArgs>(
RemoveAndRepublish, EventPhases.Initiated);
... and my handler method is as follows:
public void RemoveAndRepublish(Component cmp, UnPublishEventArgs args,
EventPhases phase)
{
// ... code to locate related component, and perform required actions...
var instruction = new PublishInstruction(cmp.Session)
{
DeployAt = DateTime.Now,
RenderInstruction = new RenderInstruction(cmp.Session)
{
RenderMode = RenderMode.Publish
},
ResolveInstruction = new ResolveInstruction(cmp.Session)
{
IncludeComponentLinks = true
},
RollbackOnFailure = true,
StartAt = DateTime.MinValue
};
var target = args.Targets.FirstOrDefault();
PublishEngine.Publish(new[] {related}, instruction, new[] {target});
}
My problem is that the UnPublishEventArgs.Targets property is an IList<PublishingTarget>, which at runtime turns out to be a List<TargetType>, and I need to get a PublicationTarget object to be able to call PublishEngine.Publish(...).
My question is: is there a way to get the current (un-)PublicationTarget from an UnPublish event?
Can anyone offer any help?
You will need to figure out the PublicationTarget(s) yourself from the TargetType(s). Based on the TargetType and the Publication of the item you will need to iterate through the PublicationTargets to see if they allow your Publication to publish to them. This will (eventually) give you a list of PublicationTargets. Unfortunately that is quite a lot of work :(
All that said, do you really need the PublicationTarget? If you need to republish or un-publish items from the same Publication/Target combination, you should be able to parse the same TargetType array to the PublishEngine.Publish(...) method.
When I search the docs, I see the following override on PublishEngine():
PublishEngine.Publish Method (IEnumerable<(Of <(<'IdentifiableObject>)>)>, PublishInstruction, IEnumerable<(Of <(<'TargetType>)>)>, PublishPriority)
public static ICollection<PublishTransaction> Publish(
IEnumerable<IdentifiableObject> items,
PublishInstruction publishInstruction,
IEnumerable<TargetType> targetTypes,
PublishPriority priority
)
I have an extension function as such...
public static class EventLibrary
{
[EventCollection]
public static Event Sequence(this Event ev)
{
ev.Started += (args) =>
{
// do something!
}
}
}
I then, inside Event, I look at the delegate subscribers using the following...
var dels = new List<Delegate[]>();
if (Started != null)
dels.Add(Started.GetInvocationList());
The reason is to try and detect whether the function that created the closure has an attribute, as in this example, EventCollection. On the Delegate object, both DelcaringType and ReflectedType return something like EventLibrary+<Sequence>c_AnonStorey1 but this is as far as I get.
I would love to do this without any string operations but I'm not sure it's possible... Does anyone know?
I believe there is no way to do that reliably. The closest you can do is to get the DeclaringType, but there isn't anything like DeclaringMethod.
It seems you already noticed you could try using the name of the lambda method, but doing so would be fragile (what about method overloads?) and might not work the same in other languages (like VB.NET) or future versions of the compiler.
I think the best way to do this is to somehow configure Event to tell it what you want. Possibly something like:
var eventCollectionEvent = ev.EventCollection;
eventCollectionEvent.Started += …;
I just started using C# this afternoon, so be a little gentle.
Currently I am working on a type of "template engine" where one of the callbacks needs to generate a globally unique ID. I am using delegates to manage the callbacks.
Currently the code looks like this (though I have also tried an anonymous function & returning NewGuid directly w/o a variable):
static string UID(List<string> p)
{
string s = Guid.NewGuid().ToString();
return s;
}
Which, when called directly, works fine. However if I try to call it via the delegate (added to a StringDictionary via addCallback("generate UID", new CallbackWrapper(UID))), the program will generate the same GUID regardless of how many times I duplicate it; even though calling the method directly both before & after the event occurs results in a unique ID as expected. I'v
No doubt it's just something simple I've missed, inevitably stemming from me being relatively inexperienced at C#.
Any help would be appreciated.
Thanks.
Well, I've now tried Dictionary with the same result.
CallbackWrapper is just the delegate, it's defined like this:
delegate string CallbackWrapper(List<string> parameters);
The remainder of the work is done in another class, which looks like this:
class TemplateParser
{
private Dictionary<string, CallbackWrapper> callbackMap;
public TemplateParser(string directivePrefix, string directiveSuffix)
{
...
callbackMap = new Dictionary<string,CallbackWrapper>();
}
public TemplateParser() : this("<!-- {", "} -->") {}
{
callbackMap.Add(name, callback);
}
public string parse(string filename)
{
...
string replacement =
callbackMap[directiveName](new List<string>(parameters.Split(new string[] { ";", " " }, StringSplitOptions.RemoveEmptyEntries));
...
}
}
I've stripped out the majority of the string handling code to save some space.
The issue is in your calling code, not in the code itself, nor in the delegate.
Using delegates here definitely works if called correctly.
Furthermore, your code can be slightly simplified:
static string UID(List<string> p)
{
return Guid.NewGuid().ToString();
}
(The variable is utterly redundant.)
use delegate.invoke
The difference between direct function call and delegate.invoke is here
http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/f629c34d-6523-433a-90b3-bb5d445c5587
StringDictionary will automatically cast your CallbackWrapper to a string, meaning it will only run once and store the output of CallbackWrapper.ToString(). This is probably not what you want.
Try using Dictionary<string, CallbackWrapper> instead.