I'm trying to follow the TTNavigatorDemo to implement TTNavigator on a MonoTouch app.
My first question is what do I assign to PersistenceMode (I was expecting an enum but it asks me for an int!):
Navigator = new TTNavigator();
Navigator.Window = window;
Navigator.PersistenceMode = ???;
Second,
I've declared a url map:
Navigator.URLMap.FromToSharedViewController("tt://tabBar", Class.GetHandle(typeof(MainTabBarController)));
But when I try to navigate to it by calling Navigator.OpenURL(#"tt://tabBar", false); I'm getting an error:
-[TTNavigator openURL:animated:]: unrecognized selector sent to instance 0x9c45120
I've also tried to register the mapping with a Selector:
Navigator.URLMap.FromToSharedViewController("tt://tabBar", Class.GetHandle(typeof(MainTabBarController)), new NSObject(Selector.Init));
Any pointers on how to get TTNavigator to work on Monotouch? Or is there a better alternative?
Related
I am new to C# and to IOptionsMonitor concept
MyOptionsClass myOptionObject = new MyOptionClass ();
IOptions<MyOptionsClass> instaceOfIOptions =
new OptionsWrapper<MyOptionsClass>(myOptionObject );
Similar to the approach they have used to get an instance of "IOptions"
I wanted to get of IOptionsMonitor So I tried in similar way but it gives me an compiler error.
IOptionsMonitor<CosmosDBOptions> instaceOfIOptionsMonitor =
new OptionsMonitor<CosmosDBOptions>(myOptionObject);
But it asks for 3 arguments (factory, source and cache) in place of myOptionObject
what do I need to pass for the arguments here?
How to achieve this?
I am attempting to control two browser windows via selenium using c# and a single chromedriver. The reason being that I need to share session details accross browser windows.
The code that I have tried and failed with is below;
var options = new ChromeOptions();
options.AddArguments("chrome.switches", "--disable-extensions --disable-extensions-file-access-check --disable-extensions-http-throttling --disable-infobars --enable-automation ");
options.AddUserProfilePreference("credentials_enable_service", false);
options.AddUserProfilePreference("profile.password_manager_enabled", false);
options.PageLoadStrategy = PageLoadStrategy.Default;
ChromeDriverService service = ChromeDriverService.CreateDefaultService();
service.HideCommandPromptWindow = true;
var Driver = new ChromeDriver(service, options);
//THIS WILL OPEN A NEW WINDOW. BUT BECAUSE IT IS A NEW DRIVER DOES NOT WORK FOR SHARING SESSION DETAILS.
//var TestDriver = new ChromeDriver(service, options);
//TestDriver.Manage().Window.Maximize();
//THIS JUST OPENS UP A NEW TAB. NOT A NEW WINDOW (IT WOULD SEEM MOST DOCUMENTATION SUGGESTS THAT IT SHOULD)
IJavaScriptExecutor jscript = Driver as IJavaScriptExecutor;
jscript.ExecuteScript("window.open();", "google.com.au");
//TRY USING THE SEND KEYS TECHNIQUE. NOTHING HAPPENS
var test = Driver.FindElement(By.TagName("html"));
test.SendKeys(Keys.Control + "n");
test.SendKeys(Keys.Control + "t");
//TRY AGAIN USING THE SEND KEYS TECHNIQUE USING A DIFFERENT TAG. NOTHING HAPPENS
var blah = Driver.FindElements(By.TagName("body"));
blah[0].SendKeys(Keys.Control + "t");
//TRY USING ACTIONS. NOTHING HAPPENS
Actions action = new Actions(Driver);
action.SendKeys(OpenQA.Selenium.Keys.Control + "n");
action.Build().Perform();
I may resort to AutoIt to open a browser if I have to, but one more dependency is not what I need. Documentation everywhere around the web seems to suggest than all the options I tried above should work...I suspect it may be a chromedriver issue of some kind.
Any ideas on how to achieve my goal would be greatly appreciated
UPDATE.
Arnons answer below lead me to the solution. If you are in a similar situation the best thing to do is just open up the browser console (from developers tools) and experiment with javascript until you get what you want. Then just execute that. In the end executing the following code has worked for me.
IJavaScriptExecutor jscript = Driver as IJavaScriptExecutor;
jscript.ExecuteScript("window.open('https://www.bing.com.au','_blank','toolbar = 0, location = 0, menubar = 0')");
The other alternative was to use Autoit, which I also got working, much easier than I did figuring out the javascript. But one less dependency is best :)
UPDATE2.
Further complications arise with trying to control the window as an independent browser window. I believe any new window created from a parent window, has the same process id (at least my testing has indicated so), and for all intense and purpose is treated as a tab in the selinium driver. I therefore conclude that certain things are just not possible (for example relocating the child browser window on the screen).
Your first attempt using ExecuteJavaScript was very close, but In order for it to open a new window instead of new tab, you should add the following arguments: `"_blank", "toolbar=0,location=0,menubar=0" to it.
See this question for more details.
I should have read the question better, here is my solution. Ended up using this for selecting windows that popped up after clicking a button but should work with swapping between windows.
//---- Setup Handles ----
//Create a Handle to come back to window 1
string currentHandle = driver.CurrentWindowHandle;
//Creates a target handle for window 2
string popupWindowHandle = wait.Until<string>((d) =>
{
string foundHandle = null;
// Subtract out the list of known handles. In the case of a single
// popup, the newHandles list will only have one value.
List<string> newHandles = driver.WindowHandles.Except(originalHandles).ToList();
if (newHandles.Count > 0)
{
foundHandle = newHandles[0];
}
return foundHandle;
});
//Now you can use these next 2 lines to continuously swap
//Swaps to window 2
driver.SwitchTo().Window(popupWindowHandle);
// Do stuff here in second window
//Swap back to window 1
driver.SwitchTo().Window(currentHandle);
// Do stuff here in first window
You need to explicitly tell Selenium which tab you wish to interact with, which in this case would be;
driver.SwitchTo().Window(driver.WindowHandles.Last());
Today I updated to Xamarin.iOS 8.6.0.51 and switched to the new Unified API.
Now I want to get the keyboard size (this code worked before):
var val = new NSValue (notification.UserInfo.ValueForKey (UIKeyboard.FrameBeginUserInfoKey).Handle);
RectangleF keyboardSize = val.RectangleFValue;
With the help of the migration tool the RectangleF is converted to CGRect, but the errors I'm getting here are
Error CS1540: Cannot access protected member
Foundation.NSValue.NSValue(System.IntPtr)' via a qualifier of type
Foundation.NSValue'. The qualifier must be of type
`MyApp.SomeViewController' or derived from it (CS1540)
and
Error CS0122: `Foundation.NSValue.NSValue(System.IntPtr)' is
inaccessible due to its protection level (CS0122)
How can I solve this? I can delete new NSValue(...), but RectangleFValue still doesn't work and I would need a replacement/another way.
Edit:
According to jonathanpeppers I modified my code to:
NSValue keyboardFrameBegin = (NSValue)notification.UserInfo.ValueForKey (UIKeyboard.FrameBeginUserInfoKey);
CGRect keyboardSize = keyboardFrameBegin.CGRectValue;
This doesn't throw an error anymore, but I can't test it further because I'm in the process of migrating to the Unified API and there are still some errors to correct.
Can you just use a cast:
var val = (NSValue)notification.UserInfo.ValueForKey (UIKeyboard.FrameBeginUserInfoKey);
RectangleF keyboardSize = val.RectangleFValue;
Only rarely should you have to mess with handles in Xamarin.iOS.
To be absolutely sure what to cast to, debug and see what the underlying type is first:
NSObject val = notification.UserInfo.ValueForKey (UIKeyboard.FrameBeginUserInfoKey);
Console.WriteLine("Type: " + val.GetType());
The .ctor(IntPtr) constructors in NSObject could be misused and causes issue later. Also, like #jonathanpeppers mentioned, they were not required for many scenarios (like this one).
Another way to do this (e.g. if you have nothing else but an handle), as documented here, is to use the ObjCRuntime.Runtime.GetNSObject<T> (IntPtr) method. That will ensure the NSObject-subclass is created correctly.
I am creating an application that interfaces with Google's Maps API v3. My current approach is using a WebBrowser control by WebBrowser.Navigate("Map.html"). This is working correctly at the moment; however, I am also aware of WebBrowser.InvokeScript(). I have seen this used to execute a javascript function, but I would like to have something like the following structure:
APICalls.js - Contains different functions that can be called, or even separated out into a file for each function if necessary.
MapInterface.cs
WebBrowser.InvokeScript("APICalls.js", args) - Or control the javascript variables directly.
I have seen the InvokeScript method used, but none of the examples gave any detail to the source of the function, so I'm not sure if it was calling it from an html file or js file. Is it possible to have a structure like this, or a similarly organized structure, rather than creating an html file with javascript in each one and using Navigate()?
Additionally, are there any easier ways to use Google Maps with WPF. I checked around, but all of the resources I found were at least 2-3 years old, which I believe is older than the newest version of the maps API.
I can't suggest a better way of using Google Maps API with WPF (although I'm sure it exists), but I can try to answer the rest of the question.
First, make sure to enable FEATURE_BROWSER_EMULATION for your WebBrowser app, so Google Maps API recognizes is it as modern HTML5-capable browser.
Then, navigate to your "Map.html" page and let it finish loading. Here's how it can be done using async/await (the code is for the WinForms version of WebBrowser control, but the concept remains the same).
You can have your APICalls.js as a separate local file, but you'd need to create and populate a <script> element for it from C#. You do it once for the session.
Example:
var scriptText = File.ReadAllText("APICalls.js");
dynamic htmlDocument = webBrowser.Document;
var script = htmlDocument.createElement("script");
script.type = "text/javascript";
script.appendChild(htmlDocument.createTextNode(scriptText));
htmlDocument.body.appendChild(script);
Then you can call functions from this script in a few different ways.
For example, your JavaScript entry point function in APICalls.js may look like this:
(function() {
window.callMeFromCsharp = function(arg1, arg2) {
window.alert(arg1 + ", " +arg2);
}
})();
Which you could call from C# like this:
webBrowser.InvokeScript("callMeFromCsharp", "Hello", "World!");
[UPDATE] If you're looking for a bit more modular or object-oriented approach, you can utilize the dynamic feature of C#. Example:
JavaScript:
(function() {
window.apiObject = function() {
return {
property: "I'm a property",
Method1: function(arg) { alert("I'm method 1, " + arg); },
Method2: function() { return "I'm method 2"; }
};
}
})();
C#:
dynamic apiObject = webBrowser.InvokeScript("apiObject");
string property = apiObject.property;
MessageBox.Show(property);
apiObject.Method1("Hello!");
MessageBox.Show(apiObject.Method2());
My plan:
I'm trying to setup my C# project to communicate with Nodebox to call a certain function which populates a graph and draws it in a new window.
Current situation: [fixed... see Update2]
I have already included all python-modules needed, but im still getting a
Library 'GL' not found
it seems that the pyglet module needs a reference to GL/gl.h, but can't find it due to IronPython behaviour.
Requirement:
The project needs to stay as small as possible without installing new packages. Thats why i have copied all my modules into the project-folder and would like to keep it that or a similar way.
My question:
Is there a certain workaround for my problem or a fix for the library-folder missmatch.
Have read some articles about Tao-Opengl and OpenTK but can't find a good solution.
Update1:
Updated my sourcecode with a small pyglet window-rendering example. Problem is in pyglet and referenced c-Objects. How do i include them in my c# project to be called? No idea so far... experimenting alittle now. Keeping you updated.
SampleCode C#:
ScriptRuntimeSetup setup = Python.CreateRuntimeSetup(null);
ScriptRuntime runtime = new ScriptRuntime(setup);
ScriptEngine engine = Python.GetEngine(runtime);
ScriptSource source = engine.CreateScriptSourceFromFile("test.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
SampleCode Python (test.py):
from nodebox.graphics import *
from nodebox.graphics.physics import Vector, Boid, Flock, Obstacle
flock = Flock(50, x=-50, y=-50, width=700, height=400)
flock.sight(80)
def draw(canvas):
canvas.clear()
flock.update(separation=0.4, cohesion=0.6, alignment=0.1, teleport=True)
for boid in flock:
push()
translate(boid.x, boid.y)
scale(0.5 + boid.depth)
rotate(boid.heading)
arrow(0, 0, 15)
pop()
canvas.size = 600, 300
def main(canvas):
canvas.run(draw)
Update2:
Line 139 [pyglet/lib.py] sys.platform is not win32... there was the error. Fixed it by just using the line:
from pyglet.gl.lib_wgl import link_GL, link_GLU, link_WGL
Now the following Error:
'module' object has no attribute '_getframe'
Kind of a pain to fix it. Updating with results...
Update3:
Fixed by adding following line right after first line in C#-Code:
setup.Options["Frames"] = true;
Current Problem:
No module named unicodedata, but in Python26/DLLs is only a *.pyd file`. So.. how do i implement it now?!
Update4:
Fixed by surfing: link text and adding unicodedata.py and '.pyd to C# Projectfolder.
Current Problem:
'libGL.so not found'... guys.. im almost giving up on nodebox for C#.. to be continued
Update5:
i gave up :/ workaround: c# communicating with nodebox over xml and filesystemwatchers. Not optimal, but case solved.
-X:Frames enables the frames option as runtime (it slows code down a little to have access to the Python frames all the time).
To enable frames when hosting you just need to do:
ScriptRuntimeSetup setup = Python.CreateRuntimeSetup(new Dictionary<string, object>() {
{ "Frames", true }
});
Instead of the null that you're passing now. That's just creating a new dictionary for the options dictionary w/ the contents "Frames" set to true. You can set other options in there as well and in general the -X:Name option is the same here as it is for the command line.