Im trying to update an object in my backend. I have created this backend methode
// PUT: api/Appointment/5
/// <summary>
/// Modifies an appointment
/// </summary>
/// <param name="appointmentId">id of the appointment to be modified</param>
/// <param name="start">Start of appointment</param>
/// <param name="end">End of appointment</param>
[HttpPut("{appointmentId}")]
public IActionResult PutAppointment(int appointmentId, string start)
{
if(start == null) {
Console.WriteLine("null");
}
But my start paramater that i return from my angular app is alway null.
This is my angular methode:
updateAppointment(
appointmentId:number, start: string
): Observable<any>{
console.log(start);
const params = new HttpParams()
.set('start', start);
return this.http.put(
`${environment.apiUrl}/appointment/${appointmentId}`,
{params},
{ responseType: "text"})
}
I tried multiple things, but all of them return start with a null value. I log it to the console in my angular app and there it gives the right value of the string, but in my backend (asp net) it always is null.
Anyone knows how I can fix this?
Related
I have a simple and basic question: how do I make my app save changes on the textbox and other editable tools (like radiobuttons/colors etc)?
I am coding a UWP app on Visual Studio.
When I lunch the app on VS, the text I write in the textboxes disapear when I close the app.
Sorry I just started a few days ago and can't find a solution...
Thanks!
you need to store that data locally, when you closing your app. so when you restart app first fetch data from that local storage and save or append it in your textbox.
You can use below two ways to store it.
Create one text file and store your data in it, so you can fetch data whenever your app
is restarted.
you can use settings for store local data. please check below link for more information.
https://learn.microsoft.com/en-us/windows/uwp/get-started/settings-learning-track
By localSettings, You can store your data locally in your machine.
public static class LocalSettingsHelper
{
private static ApplicationDataContainer _localSettings = ApplicationData.Current.LocalSettings;
/// <summary>
/// Create Local Settings storage Container
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="container">Container</param>
/// <param name="containerValue">ContainerValue</param>
/// <param name="value">Value</param>
internal static void SetContainer<T>(string container, string containerValue, T value)
{
var containerName = _localSettings.CreateContainer(container, ApplicationDataCreateDisposition.Always);
_localSettings.Containers[container].Values[containerValue] = value != null ? JsonConvert.SerializeObject(value) : null;
}
/// <summary>
/// Get Local Settings Container
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="container">Container</param>
/// <param name="containerValue">ContainerValue</param>
/// <returns>Value as Type</returns>
internal static T GetContainerValue<T>(string container, string containerValue)
{
var containerName = _localSettings.CreateContainer(container, ApplicationDataCreateDisposition.Always);
string currentValue = _localSettings.Containers[container].Values[containerValue] as string;
if (currentValue == null)
{
return default(T);
}
return JsonConvert.DeserializeObject<T>(currentValue);
}
}
I am trying to set MailMerge fields and let Word fill them for me, which isn't a Problem at all... What i am looking to do and can't figure out is the following, I want to set 2 MailMergeFields in 1 place and let Word sort it out for me.
In this case have a mergefield for PO_Box and Adress, if there is a PO_Box # use it, otherwise use the Standard Adress.
Example of the MailMerge what it would look like in Word:
{ IF { MERGEFIELD PO_Box } > "1" "{ MERGEFIELD PO_Box }" "{ MERGEFIELD Adress }" \* MERGEFORMAT }
Is there a way to make this happen thru some Word Interop Funktion?
Edit:
static void Main(string[] args)
{
object fileName = #"C:\test.docx";
string dataSource = #"C:\Test.csv";
Word.Selection wrdSelection;
Word.MailMerge wrdMailMerge;
Word.MailMergeFields wrdMergeFields;
// Start Word Application
Word.Application wrdApp = new Word.Application();
//Load a document
Word.Document wrdDoc = wrdApp.Documents.Add(ref fileName, Visible: true);
wrdSelection = wrdApp.Selection;
wrdMailMerge = wrdDoc.MailMerge;
// Open Data Source from .csv file
wrdDoc.MailMerge.OpenDataSource(dataSource);
//Create MergeFields
wrdSelection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft;
wrdSelection.ParagraphFormat.LineSpacingRule = Word.WdLineSpacing.wdLineSpaceSingle;
wrdSelection.ParagraphFormat.SpaceAfter = 0.0F;
wrdMergeFields = wrdMailMerge.Fields;
wrdMergeFields.Add(wrdSelection.Range, "Title");
wrdSelection.TypeText(" ");
wrdMergeFields.Add(wrdSelection.Range, "FirstName");
wrdSelection.TypeText(" ");
wrdMergeFields.Add(wrdSelection.Range, "LastName");
wrdSelection.TypeParagraph();
// Here I want to combine this Field with a PO_Box and let Word
// do the trick
wrdMergeFields.Add(wrdSelection.Range, "Address");
wrdSelection.TypeParagraph();
wrdMergeFields.Add(wrdSelection.Range, "City");
wrdSelection.TypeText(", ");
wrdMergeFields.Add(wrdSelection.Range, "State");
wrdSelection.TypeText(" ");
wrdMergeFields.Add(wrdSelection.Range, "Zip");
wrdSelection.ParagraphFormat.LineSpacingRule = Word.WdLineSpacing.wdLineSpaceDouble;
insertLines(wrdApp, 2);
//Right justify the line and insert a date field with current date.
wrdSelection.ParagraphFormat.Alignment = Word.WdParagraphAlignment.wdAlignParagraphRight;
object objDate = "dd.MM.yyyy";
wrdSelection.InsertDateTime(ref objDate);
//Preview the final merge
wrdDoc.MailMerge.Destination = Word.WdMailMergeDestination.wdSendToNewDocument;
wrdDoc.MailMerge.Execute();
//Close Template
object saveOption = Word.WdSaveOptions.wdDoNotSaveChanges;
wrdDoc.Close(ref saveOption);
//Shows the Application after the process to the User
wrdApp.Visible = true;
}
public static void insertLines(Word.Application wrdApp, int LineNum)
{
int iCount;
// Insert "LineNum" blank lines.
for (iCount = 1; iCount <= LineNum; iCount++)
{
wrdApp.Selection.TypeParagraph();
}
}
So this basicly what I have, now i need the Adress MergeField to behave as i described above, since I will receive a .csv data from another programm that i can't modify I would like to place this field in Word that it will sort out if there is a PO Box or Adress.
So, what you really want is to create the nested field codes. There are two basic approaches for this:
Record a macro while doing it as a user as the basis. This relies on the Selection object, which can be tricky; the approach is not scalable (only works for that specific combination). This is described on StackOverflow, so I won't repeat it here: Setting up a nested field in Word using VBA
Insert the field as a string, using "placeholders" to indicate
where the field codes are, then convert the placeholders to field
codes. This is scalable: it can be used for any combination of
fields. There is an excellent algorithm in C# posted on GitHub, by Florian Wolters in response to a discussion in which I participated on MSDN. I copy it below for convenience.
https://gist.github.com/FlorianWolters/6257233
//------------------------------------------------------------------------------
// <copyright file="FieldCreator.cs" company="Florian Wolters">
// Copyright (c) Florian Wolters. All rights reserved.
// </copyright>
// <author>Florian Wolters <wolters.fl#gmail.com></author>
//------------------------------------------------------------------------------
namespace FlorianWolters.Office.Word.Fields
{
using System;
using System.Collections;
using System.Runtime.InteropServices;
using Word = Microsoft.Office.Interop.Word;
/// <summary>
/// The class <see cref="FieldCreator"/> simplifies the creation of <see cref="Word.Field"/>s.
/// </summary>
public class FieldCreator
{
/// <summary>
/// Adds one or more new <see cref="Word.Field"/> to the specified <see cref="Word.Range"/>.
/// <para>
/// This method allows to insert nested fields at the specified range.
/// </para>
/// <example>
/// <c>InsertField(Application.Selection.Range, {{= {{PAGE}} - 1}};</c>
/// will produce
/// { = { PAGE } - 1 }
/// </example>
/// </summary>
/// <param name="range">The <see cref="Word.Range"/> where to add the <see cref="Word.Field"/>.</param>
/// <param name="theString">The string to convert to one or more <see cref="Word.Field"/> objects.</param>
/// <param name="fieldOpen">The special code to mark the start of a <see cref="Word.Field"/>.</param>
/// <param name="fieldClose">The special code to mark the end of a <see cref="Word.Field"/>.</param>
/// <returns>The newly created <see cref="Word.Field"/></returns>
/// <remarks>
/// A solution for VBA has been taken from this
/// article and adopted for C# by the author.
/// </remarks>
public Word.Field InsertField(
Word.Range range,
string theString = "{{}}",
string fieldOpen = "{{",
string fieldClose = "}}")
{
if (null == range)
{
throw new ArgumentNullException("range");
}
if (string.IsNullOrEmpty(fieldOpen))
{
throw new ArgumentException("fieldOpen");
}
if (string.IsNullOrEmpty(fieldClose))
{
throw new ArgumentException("fieldClose");
}
if (!theString.Contains(fieldOpen) || !theString.Contains(fieldClose))
{
throw new ArgumentException("theString");
}
// Special case. If we do not check this, the algorithm breaks.
if (theString == fieldOpen + fieldClose)
{
return this.InsertEmpty(range);
}
// TODO Implement additional error handling.
// TODO Possible to remove the dependency to state capture?
using (new StateCapture(range.Application.ActiveDocument))
{
Word.Field result = null;
Stack fieldStack = new Stack();
range.Text = theString;
fieldStack.Push(range);
Word.Range searchRange = range.Duplicate;
Word.Range nextOpen = null;
Word.Range nextClose = null;
Word.Range fieldRange = null;
while (searchRange.Start != searchRange.End)
{
nextOpen = this.FindNextOpen(searchRange.Duplicate, fieldOpen);
nextClose = this.FindNextClose(searchRange.Duplicate, fieldClose);
if (null == nextClose)
{
break;
}
// See which marker comes first.
if (nextOpen.Start < nextClose.Start)
{
nextOpen.Text = string.Empty;
searchRange.Start = nextOpen.End;
// Field open, so push a new range to the stack.
fieldStack.Push(nextOpen.Duplicate);
}
else
{
nextClose.Text = string.Empty;
// Move start of main search region onwards past the end marker.
searchRange.Start = nextClose.End;
// Field close, so pop the last range from the stack and insert the field.
fieldRange = (Word.Range)fieldStack.Pop();
fieldRange.End = nextClose.End;
result = this.InsertEmpty(fieldRange);
}
}
// Move the current selection after all inserted fields.
// TODO Improvement possible, e.g. by using another range object?
int newPos = fieldRange.End + fieldRange.Fields.Count + 1;
fieldRange.SetRange(newPos, newPos);
fieldRange.Select();
// Update the result of the outer field object.
result.Update();
return result;
}
}
/// <summary>
/// Adds a new empty <see cref="Word.Field"/> to the specified <see cref="Word.Range"/>.
/// </summary>
/// <param name="range">The <see cref="Word.Range"/> where to add the <see cref="Word.Field"/>.</param>
/// <param name="preserveFormatting">
/// Whether to apply the formatting of the previous <see cref="Word.Field"/> result to the new result.
/// </param>
/// <returns>The newly created <see cref="Word.Field"/>.</returns>
public Word.Field InsertEmpty(Word.Range range, bool preserveFormatting = false)
{
Word.Field result = this.AddFieldToRange(range, Word.WdFieldType.wdFieldEmpty, preserveFormatting);
// Show the field codes of an empty field, because otherwise we can't be sure that it is visible.
result.ShowCodes = true;
return result;
}
/// <summary>
/// Creates a <see cref="Word.Field"/> and adds it to the specified <see cref="Word.Range"/>
/// </summary>
/// <remarks>
/// The <see cref="Word.Field"/> is added to the <see cref="Word.Fields"/> collection of the specified <see
/// cref="Word.Range"/>.
/// </remarks>
/// <param name="range">The <see cref="Word.Range"/> where to add the <see cref="Word.Field"/>.</param>
/// <param name="type">The type of <see cref="Word.Field"/> to create.</param>
/// <param name="preserveFormatting">
/// Whether to apply the formatting of the previous field result to the new result.
/// </param>
/// <param name="text">Additional text needed for the <see cref="Word.Field"/>.</param>
/// <returns>The newly created <see cref="Word.Field"/>.</returns>
private Word.Field AddFieldToRange(
Word.Range range,
Word.WdFieldType type,
bool preserveFormatting = false,
string text = null)
{
return range.Fields.Add(
range,
type,
(null == text) ? Type.Missing : text,
preserveFormatting);
}
private Word.Range FindNextOpen(Word.Range range, string text)
{
Word.Find find = this.CreateFind(range, text);
Word.Range result = range.Duplicate;
if (!find.Found)
{
// Make sure that the next closing field will be found first.
result.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
}
return result;
}
private Word.Range FindNextClose(Word.Range range, string text)
{
return this.CreateFind(range, text).Found ? range.Duplicate : null;
}
private Word.Find CreateFind(Word.Range range, string text)
{
Word.Find result = range.Find;
result.Execute(FindText: text, Forward: true, Wrap: Word.WdFindWrap.wdFindStop);
return result;
}
}
}
I'm looking for a way to programmatically get the summary portion of Xml-comments of a method in ASP.net.
I have looked at the previous related posts and they do not supply a way of doing so in a web environment.
I can not use any 3rd party apps and due to a web environment, Visual studio plugin's aren't much use either.
The closest thing I have found to a working solution was the JimBlackler project, but it only works on DLL's.
Naturally, something like 'supply .CS file, get XML documentation' would be optimal.
Current situation
I have a web-service and trying to dynamically generate documentation for it.
Reading the Methods, and properties is easy, but getting the Summary for each method is throwing me off a bit.
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
/// <summary>
/// This Is what I'm trying to read
/// </summary>
public void SomeMethod()
{
}
}
A Workaround - Using reflection on Program.DLL/EXE together with Program.XML file
If you take a look at the sibling .XML file generated by Visual Studio you will see that there is a fairly flat hierarchy of /members/member.
All you have to do is get hold on each method from your DLL via MethodInfo object. Once you have this object you turn to the XML and use XPATH to get the member containing the XML documentation for this method.
Members are preceded by a letter. XML doc for methods are preceded by "M:" for class by "T:" etc.
Load your sibling XML
string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";
if (File.Exists(docuPath))
{
_docuDoc = new XmlDocument();
_docuDoc.Load(docuPath);
}
Use this xpath to get the member representing the method XML docu
string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;
XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
"//member[starts-with(#name, '" + path + "')]");
Now scan childnodes for all the rows of "///"
Sometimes the /// Summary contains extra blanks, if this bothers use this to remove
var cleanStr = Regex.Replace(row.InnerXml, #"\s+", " ");
The XML summary isn't stored in the .NET assembly - it's optionally written out to an XML file as part of your build (assuming you're using Visual Studio).
Consequently there is no way to "pull out" the XML summaries of each method via reflection on a compiled .NET assembly (either .EXE or .DLL) - because the data simply isn't there for you to pull out. If you want the data, you'll have to instruct your build environment to output the XML files as part of your build process and parse those XML files at runtime to get at the summary information.
You could 'document' your method using the System.ComponentModel.DataAnnotations.DisplayAttribute attribute, e.g.
[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}
then use reflection to pull the description at runtime.
A deleted post, made by #OleksandrIeremenko, on this thread links to this article https://jimblackler.net/blog/?p=49 which was the basis for my solution.
Below is a modification of Jim Blackler's code making extension methods off the MemberInfo and Type objects and adding code that returns the summary text or an empty string if not available.
Usage
var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();
Extension Class
/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumentationExtensions
{
/// <summary>
/// Provides the documentation comments for a specific method
/// </summary>
/// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
/// <returns>The XML fragment describing the method</returns>
public static XmlElement GetDocumentation(this MethodInfo methodInfo)
{
// Calculate the parameter string as this is in the member name in the XML
var parametersString = "";
foreach (var parameterInfo in methodInfo.GetParameters())
{
if (parametersString.Length > 0)
{
parametersString += ",";
}
parametersString += parameterInfo.ParameterType.FullName;
}
//AL: 15.04.2008 ==> BUG-FIX remove “()” if parametersString is empty
if (parametersString.Length > 0)
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
else
return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
}
/// <summary>
/// Provides the documentation comments for a specific member
/// </summary>
/// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
/// <returns>The XML fragment describing the member</returns>
public static XmlElement GetDocumentation(this MemberInfo memberInfo)
{
// First character [0] of member type is prefix character in the name in the XML
return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
}
/// <summary>
/// Returns the Xml documenation summary comment for this member
/// </summary>
/// <param name="memberInfo"></param>
/// <returns></returns>
public static string GetSummary(this MemberInfo memberInfo)
{
var element = memberInfo.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Provides the documentation comments for a specific type
/// </summary>
/// <param name="type">Type to find the documentation for</param>
/// <returns>The XML fragment that describes the type</returns>
public static XmlElement GetDocumentation(this Type type)
{
// Prefix in type names is T
return XmlFromName(type, 'T', "");
}
/// <summary>
/// Gets the summary portion of a type's documenation or returns an empty string if not available
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string GetSummary(this Type type)
{
var element = type.GetDocumentation();
var summaryElm = element?.SelectSingleNode("summary");
if (summaryElm == null) return "";
return summaryElm.InnerText.Trim();
}
/// <summary>
/// Obtains the XML Element that describes a reflection element by searching the
/// members for a member that has a name that describes the element.
/// </summary>
/// <param name="type">The type or parent type, used to fetch the assembly</param>
/// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
/// <param name="name">Where relevant, the full name qualifier for the element</param>
/// <returns>The member that has a name that describes the specified reflection element</returns>
private static XmlElement XmlFromName(this Type type, char prefix, string name)
{
string fullName;
if (string.IsNullOrEmpty(name))
fullName = prefix + ":" + type.FullName;
else
fullName = prefix + ":" + type.FullName + "." + name;
var xmlDocument = XmlFromAssembly(type.Assembly);
var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[#name='" + fullName + "']") as XmlElement;
return matchedElement;
}
/// <summary>
/// A cache used to remember Xml documentation for assemblies
/// </summary>
private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();
/// <summary>
/// A cache used to store failure exceptions for assembly lookups
/// </summary>
private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();
/// <summary>
/// Obtains the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
/// <remarks>This version uses a cache to preserve the assemblies, so that
/// the XML file is not loaded and parsed on every single lookup</remarks>
public static XmlDocument XmlFromAssembly(this Assembly assembly)
{
if (FailCache.ContainsKey(assembly))
{
throw FailCache[assembly];
}
try
{
if (!Cache.ContainsKey(assembly))
{
// load the docuemnt into the cache
Cache[assembly] = XmlFromAssemblyNonCached(assembly);
}
return Cache[assembly];
}
catch (Exception exception)
{
FailCache[assembly] = exception;
throw;
}
}
/// <summary>
/// Loads and parses the documentation file for the specified assembly
/// </summary>
/// <param name="assembly">The assembly to find the XML document for</param>
/// <returns>The XML document</returns>
private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
{
var assemblyFilename = assembly.Location;
if (!string.IsNullOrEmpty(assemblyFilename))
{
StreamReader streamReader;
try
{
streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename, ".xml"));
}
catch (FileNotFoundException exception)
{
throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
}
var xmlDocument = new XmlDocument();
xmlDocument.Load(streamReader);
return xmlDocument;
}
else
{
throw new Exception("Could not ascertain assembly filename", null);
}
}
}
You can use Namotion.Reflection NuGet package to get these information:
string summary = typeof(Foo).GetXmlDocsSummary();
You can look at https://github.com/NSwag/NSwag - source for nuget NSwag.CodeGeneration - it gets summary as well, usage
var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson();
(try ILSPY decompiler against your dll, you check code and comments)
If you have access to the source code you're trying to get comments for, then you can use Roslyn compiler platform to do that. It basically gives you access to all the intermediary compiler metadata and you can do anything you want with it.
It's a bit more complicated than what other people are suggesting, but depending on what your needs are, might be an option.
It looks like this post has a code sample for something similar.
Have tried calling MoveToElement so that I can get the tooltip of a particular element. This works in Chrome fine. However, am trying to do the same thing on IE10 and Firefox 26.0 and it does hover over - but only for a split second hence not giving me enough time to get the tooltip. Putting in a sleep does not help and besides I trying to avoid thread.sleep as much as possible. My question: Is there an alternative way to hover over a field or some other expectedConditions that can be used to see if the tooltip comes up and remains there for Firefox and IE?
Code snippet:
/// <summary>
/// Check to see that the hover over option for the 'Defined' column
/// exists and also to return the text for that hover over option.
/// </summary>
/// <returns></returns>
public Tuple<bool, string[]> HoverOverDefinedColumn(bool javascriptWorkaround = false)
{
Thread.Sleep(1000);
var wait = WebDriverWaitObject();
var action = new Actions(driver);
wait.Until(d => HoverOverDefinedRow);
action.MoveToElement(HoverOverDefinedRow).MoveByOffset(5, 0);
action.Build().Perform();
var isThereAnHoverOption = HoverOverOptionExists(wait);
var textDefinedForHoverOption = TextDefined(HoverOptionText);
return new Tuple<bool, string[]>(isThereAnHoverOption, textDefinedForHoverOption);
}
/// <summary>
/// Checks to see specifically if the hover over option exists.
/// </summary>
/// <param name="wait"></param>
/// <returns></returns>
private bool HoverOverOptionExists(WebDriverWait wait)
{
var hoverOverElement =
wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector("#TTipTDnetst.hintsClass")));
return IsElementPresent(hoverOverElement);
}
/// <summary>
/// Gets the text for the hover over option.
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private string[] TextDefined(IWebElement element)
{
var path = (element.Text.Split(new string[] { " » " }, StringSplitOptions.None));
return path;
}
There are 3 different browsers being tested:
(1) Firefox
(2) Chrome
(3) Internet Explorer
(1) Firefox (v 26.0)
I still have the 'MoveToElement' problem using the Actions class. Instead, I perform a try/catch handler so that in case the actions do not work then the exception would run a Javascript method to get the tool tip as presented below:
/// <summary>
/// This is the workaround for the hover over functionality
/// for the 'Defined' column. This (for the moment) specifically
/// applies to Firefox and its inability to get the tool tip.
/// </summary>
/// <param name="elemement"></param>
private void HoverOverWorkAround(IWebElement elemement)
{
var code = "var fireOnThis = arguments[0];"
+ "var evObj = document.createEvent('MouseEvents');"
+ "evObj.initEvent( 'mouseover', true, true );"
+ "fireOnThis.dispatchEvent(evObj);";
((IJavaScriptExecutor)driver).ExecuteScript(code, elemement);
}
(2) Chrome (v 32.017)
Actions class for hover over works fine as is with the latest ChromeDriver.
(3) Internet Explorer 10
This works with the Actions class as long as I have a driver with the following options:
var options = new InternetExplorerOptions {RequireWindowFocus = true, EnablePersistentHover = false};
instance = new InternetExplorerDriver(ApplicationSettings.DriverLocation, options);
NOTE:
In the case of IE, 'NativeEvents' option is set to true by default (for Windows) and I leave that alone.
Always use 'Native Events' meaning (in this case) use the 'Actions' class to get at the hovering functionality. If this does not work then catch the exception method and run the Javascript (as for Firefox above).
The reasoning for the above approaches comes from ideas taken from the following discussion:
http://code.google.com/p/selenium/issues/detail?id=2067
update1: After more research I'm not sure this is possible, I created a UserVoice entry on fixing it.
I'm trying to save CookieContainer on app exit or when Tombstoning happens but I've run into some problems.
I've tried to save CookieContainer in the AppSettings but when loaded, the cookies are gone.
Researching this internally, DataContractSerializer cannot serialize cookies.
This seems to be a behavior that Windows Phone inherited from Silverlight's DataContractSerializer.
After doing more research it seemed like the work around was to grab the cookies from the container and save them another way. That worked fine until I hit another snag. I'm unable to GetCookies with a Uri of .mydomain.com. I belive it's because of this bug. I can see the cookie, .mydomain.com in the domaintable but GetCookies doesn't work on that particular cookie.
The bug is posted again here.
There is also a problem with getting cookies out of a container too
when the domain begins with a .:
CookieContainer container = new CookieContainer();
container.Add(new Cookie("x", "1", "/", ".blah.com"));
CookieCollection cv = container.GetCookies(new Uri("http://blah.com"));
cv = container.GetCookies(new Uri("http://w.blah.com"));
I found a work around for that using reflection to iterate the domaintable and remove the '.' prefix.
private void BugFix_CookieDomain(CookieContainer cookieContainer)
{
System.Type _ContainerType = typeof(CookieContainer);
var = _ContainerType.InvokeMember("m_domainTable",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.GetField |
System.Reflection.BindingFlags.Instance,
null,
cookieContainer,
new object[] { });
ArrayList keys = new ArrayList(table.Keys);
foreach (string keyObj in keys)
{
string key = (keyObj as string);
if (key[0] == '.')
{
string newKey = key.Remove(0, 1);
table[newKey] = table[keyObj];
}
}
}
Only, when InvokeMember is called a MethodAccessException is thrown in SL. This doesn't really solve my problem as one of the cookies I need to preserve is HttpOnly, which is one of the reasons for the CookieContainer.
If the server sends HTTPOnly cookies, you should create a
System.Net.CookieContainer on the request to hold the cookies,
although you will not see or be able to access the cookies that are
stored in the container.
So, any ideas? Am I missing something simple? Is there another way to save the state of the CookieContainer or do I need to save off the users info including password and re-authentic them every time the app starts and when coming back from tombstoning?
I have written a CookieSerializer that specifically address this issue. The serializer is pasted below. For a working project and scenario, please visit the project's CodePlex site.
public static class CookieSerializer
{
/// <summary>
/// Serializes the cookie collection to the stream.
/// </summary>
/// <param name="cookies">You can obtain the collection through your <see cref="CookieAwareWebClient">WebClient</see>'s <code>CookieContainer.GetCookies(Uri)</code>-method.</param>
/// <param name="address">The <see cref="Uri">Uri</see> that produced the cookies</param>
/// <param name="stream">The stream to which to serialize</param>
public static void Serialize(CookieCollection cookies, Uri address, Stream stream)
{
using (var writer = new StreamWriter(stream))
{
for (var enumerator = cookies.GetEnumerator(); enumerator.MoveNext();)
{
var cookie = enumerator.Current as Cookie;
if (cookie == null) continue;
writer.WriteLine(address.AbsoluteUri);
writer.WriteLine(cookie.Comment);
writer.WriteLine(cookie.CommentUri == null ? null : cookie.CommentUri.AbsoluteUri);
writer.WriteLine(cookie.Discard);
writer.WriteLine(cookie.Domain);
writer.WriteLine(cookie.Expired);
writer.WriteLine(cookie.Expires);
writer.WriteLine(cookie.HttpOnly);
writer.WriteLine(cookie.Name);
writer.WriteLine(cookie.Path);
writer.WriteLine(cookie.Port);
writer.WriteLine(cookie.Secure);
writer.WriteLine(cookie.Value);
writer.WriteLine(cookie.Version);
}
}
}
/// <summary>
/// Deserializes <see cref="Cookie">Cookie</see>s from the <see cref="Stream">Stream</see>,
/// filling the <see cref="CookieContainer">CookieContainer</see>.
/// </summary>
/// <param name="stream">Stream to read</param>
/// <param name="container">Container to fill</param>
public static void Deserialize(Stream stream, CookieContainer container)
{
using (var reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
var uri = Read(reader, absoluteUri => new Uri(absoluteUri, UriKind.Absolute));
var cookie = new Cookie();
cookie.Comment = Read(reader, comment => comment);
cookie.CommentUri = Read(reader, absoluteUri => new Uri(absoluteUri, UriKind.Absolute));
cookie.Discard = Read(reader, bool.Parse);
cookie.Domain = Read(reader, domain => domain);
cookie.Expired = Read(reader, bool.Parse);
cookie.Expires = Read(reader, DateTime.Parse);
cookie.HttpOnly = Read(reader, bool.Parse);
cookie.Name = Read(reader, name => name);
cookie.Path = Read(reader, path => path);
cookie.Port = Read(reader, port => port);
cookie.Secure = Read(reader, bool.Parse);
cookie.Value = Read(reader, value => value);
cookie.Version = Read(reader, int.Parse);
container.Add(uri, cookie);
}
}
}
/// <summary>
/// Reads a value (line) from the serialized file, translating the string value into a specific type
/// </summary>
/// <typeparam name="T">Target type</typeparam>
/// <param name="reader">Input stream</param>
/// <param name="translator">Translation function - translate the read value into
/// <typeparamref name="T"/> if the read value is not <code>null</code>.
/// <remarks>If the target type is <see cref="Uri">Uri</see> , the value is considered <code>null</code> if it's an empty string.</remarks> </param>
/// <param name="defaultValue">The default value to return if the read value is <code>null</code>.
/// <remarks>The translation function will not be called for null values.</remarks></param>
/// <returns></returns>
private static T Read<T>(TextReader reader, Func<string, T> translator, T defaultValue = default(T))
{
var value = reader.ReadLine();
if (value == null)
return defaultValue;
if (typeof(T) == typeof(Uri) && String.IsNullOrEmpty(value))
return defaultValue;
return translator(value);
}
}
You cannot access private members outside of your assembly in WP7, even with Reflection. It's a security measure put in place to ensure you cannot call internal system APIs.
It looks like you may be out of luck.