How to make work UIDocumentInteractionController in MonoTouch - c#

I am trying to open a *.epub file throught my application and I don't quite understand how to make it with the UIDocumentInteractionController class. I have seen the official IOS documentation and examples and some of the examples over the net but I don't understand how that class works. This is how I do it, what I achieve and what I dont understand:
I have a UIView with a UIButton:
using MonoTouch.UIKit;
using MonoTouch.Foundation;
using System.Drawing;
public class MyView : UIViewController
{
UIButton myBtn;
public MyView() :base()
{
View.Frame = new RectangleF(0,0,1024,768);
var myRect = new RectangleF(300,300,100,50);
myBtn = UIButton.FromType(UIButtonType.RoundedRect);
myBtn.Frame = myRect;
myBtn.TouchUpInside += delegate
{
var dic = UIDocumentInteractionController.FromUrl(new NSUrl("http://192.168.50.50:2020/myfile.epub"));
var dicDel = new UIDocumentInteractionControllerDelegate();
dic.Delegate = dicDel;
InvokeOnMainThread(delegate
{
var result = dic.PresentOpenInMenu(myRect, View, true);
//If I do this -> NullReferenceException because delegate is null (something about autorelease?? Don't know)
if(!result) dic.Delegate.DidDismissOpenInMenu(dic);
});
}
}
}
The weirdest thing is if I debug and inspect "dic" (without the delegate) before calling the PresentOpenInMenu() method it shows the menu (returning true) but just after doing it the app blows up on Main.cs because the autorelease thing I dont understand.
I am a little lost. Can someone help me understand this class and how can I make it work correctly? Thanks in advance.
EDIT: By the way, I used a *.txt file too with same results.

It looks like a MonoTouch bug. Setting the UIDocumentInteractionController.Delegate (or WeakDelegate property and then querying its value returns null (which will fail later on).
I'll look into this bug and update this answer if I can provide a workaround (until the bug is properly fixed in a future release of MonoTouch).
UPDATE: UIDocumentInteractionController already creates it's own internal UIDocumentInteractionControllerDelegate so you do not need to create one. The Delegate methods, like DidDismissOpenInMenu are available as events on UIDocumentInteractionController itself.
Remove your own Delegate (creation and set) and use the events and you should be fine.
UPDATE #2: The Delegate property returns null since the default UIDocumentInteractionControllerDelegate is not usable as is. It is meant to be inherited from and customized to do what you want to (and the unusable default one is not registred properly to be used). E.g.
class MyDocumentInteractionControllerDelegate : UIDocumentInteractionControllerDelegate { }
and
var dicDel = new MyDocumentInteractionControllerDelegate ();
would work, as in no NullReferenceException, but of course DidDismissOpenInMenu won't be doing anything interesting.

Related

Dynamically run C# code from a string, in the context of the current form (code)

I need to run code from a text file, in the context of the current form (code). One of the requirements is to have the code create and add a new control to the current form.
For example, in Form1.cs:
using System.Windows.Forms;
...
public int[] someCoords = { 20, 10 };
public string someImportantString = "Hello";
public void SayHello() {
MessageBox.Show("Hello world.");
}
private void runCodeInForm() {
// theCode will be read from a text file
string theCode = #"
// Has System.Windows.Forms already added in form
Button newButton = new Button();
newButton.Text = someImportantString; // Hello
newButton.Location = new Point(someCoords[0], someCoords[1]); // 20, 10
// Add this button to the current form
this.Controls.Add(newButton);
this.SayHello(); // Says hello. Just an example function.
";
// Execute theCode in the current form
CodeRunner.Execute(theCode, this);
}
I have tried using CSharpCodeProvider, but it seems like this can only compile the code as a separate program.
I would like this because I want the user to be able to change this code (text file) to what they would like. It is not specifically just for creating controls, but that functionality will be needed.
I am not worried about the security of the program.
Consider these points to solve the problem:
You should create your dynamic code as a class in a dll.
Your class should implement a specific interface or have a known method like Run. So you can call the method later when the code compiles.
Your class or the known method should accept some parameters to receive the context variables. These context variables can include a Form as parameter. You can also encapsulate context parameters in a class/interface or to keep it simple you can rely on dynamic for passing parameters.
Then to run the dynamic code, first compile it, then pass context parameters to the class or the known method and call the known method.
Example
Here is a quick and dirty example of how you can compile and run a code at run-time and let the code use your context:
public string SomePublicField = "Hello!";
private void button1_Click(object sender, EventArgs e) {
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] {
"mscorlib.dll",
"System.Windows.Forms.dll",
"System.dll",
"System.Drawing.dll",
"System.Core.dll",
"Microsoft.CSharp.dll"});
var results = csc.CompileAssemblyFromSource(parameters,
#"
using System.Windows.Forms;
using System.Drawing;
public class Sample
{
public void DoSomething (dynamic form)
{
var b = new Button();
b.Text = form.Text;
b.Click += (s,e)=>{MessageBox.Show(form.SomePublicField);};
form.Controls.Add(b);
}
}");
//Check if compilation is successful, run the code
if (!results.Errors.HasErrors) {
var t = results.CompiledAssembly.GetType("Sample");
dynamic o = Activator.CreateInstance(t);
o.DoSomething(this);
}
else {
var errors = string.Join(Environment.NewLine,
results.Errors.Cast<CompilerError>().Select(x => x.ErrorText));
MessageBox.Show(errors);
}
}
Basically what it seems you want to do, is dynamically compile and add code to your current program. This is not impossible and there are various ways to do it.
The most common uses for this type of functionality are plugins and scripting systems. However there are caveats with the both of these.
One of the of the biggest draw backs is that to run code that you have compiled (however it is you are doing that), you need to literally load it as an assembly into you app domain using the standard, Load methods. Once a library is loaded you actually cant unload it with out unloading the app domain, so this creates problems in certain situations .
If this is a scripting thing you are after, i would seriously consider using a pre-built scripting library (and there are many). Some use nifty-tricks to make this work well and have done a lot of the hard work for you... As an example http://csscript.net/
However be prepared! plugin and scripting systems start off easy, yet they are incredibly fiddly to make stable and workable. I would suggest exploring the domain of your problem first, and make sure you are not trying to reinvent the wheel... There are many options for serialisation and loading object parameters at run-time safely without fuss
Good luck

Microsoft Fakes: Trying to shim a class but dependencies are still there

Ok, so here's the deal: I have a complex, heavily dependent class LegacyClass that I'd like to shim so that I get rid of all its dependencies while unit testing other parts of the code base. That class creates dependencies already inside its default constructor, so I need to override it with something with no external dependencies, say, with an empty default constructor. And this is what I'm trying to do (using the Visual Studio 2013 Professional Test Framework):
using System;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyApp_Unit_Tests {
[TestClass]
public class UnitTest1 {
[TestMethod]
public void TestInstantiation1() {
using (ShimsContext.Create()) {
MyNamespace.Fakes.ShimLegacyClass.Constructor = x => { };
var legacyClassInstance = new MyNamespace.Fakes.ShimLegacyClass();
var sut = new MyNamespace.Gui.ViewModels.MainWindowViewModel(legacyClassInstance);
}
}
}
}
However, this does not work. When MainWindowViewModel is instantiated, for some reason all the same external dependencies are still required as with using the original class! Why?
The exception I'm getting, though, is System.BadImageFormatException, so I probably have some confusion about the target CPU settings, too, but anyway the root cause is that it's attempting to load the external DLL referred to only in the original (non-shimmed) legacy class in its default constructor, while I think it no longer should.
Obviously I've been misunderstood, but where's the mistake? Can I not override default constructors, after all, even with using Shims, or is my approach just wrong? What am I missing?
Thanks a million in advance for any advice!
-Seppo
I had same problem and I solved it maybe this approach going to help you
using (ShimsContext.Create())
{
LegacyClass obj=new LegacyClass();
ShimLegacyClass shimobj=new ShimLegacyClass(obj);
//
// modify every thing you want on shimobj
//
shimobj.InstanceBehavior = ShimBehaviors.Fallthrough;
//rest of test
}
This approach helps you to break dependencies in every part you want and keep the rest same as main class

module communication not working in DNN

I am using DNN6 and i creted two modules and tried to connect between them using module communicator, here is my code:
#region IntermoduleCommunication
ModuleCommunicationEventArgs oArgs = new ModuleCommunicationEventArgs();
oArgs.Value = Session["ShoppingCart"];
if (ModuleCommunication != null)
ModuleCommunication(this, oArgs);
#endregion
but i am getting 'null' in the ModuleCommunication variable?
Are you wrapping the modules in an update panel, (have the supports partial rendering option enabled) in the DNN manifest?
If I recall correctly, IMC won't work via UpdatePanels.
From whatever code you have provided, it should work. In order to get help you need to provide code for both IModuleCommunicator and IModuleListener implementation. But you can review Example implementation here. Let me know if you need more help.
Also if you are not using latest version of dnn, please try testing it by creating of latest dnn instance. Let me know if you need more help.
To get this working you need to implement the IModuleCommunicator interface. Right click on the IModuleCommunicator as showed below and extract the interface.
public partial class MyClass: PortalModuleBase, IModuleCommunicator
once extracted the following will be generated
public event ModuleCommunicationEventHandler ModuleCommunication;
I call it from a button click event
protected void btn1_Click(Object sender, EventArgs e)
{
if (ModuleCommunication == null) return;
ModuleCommunicationEventArgs args = new ModuleCommunicationEventArgs();
args.Sender = this.GetType().ToString(); ;
args.Target = "MyTarget";
}
wrap the whole thing in a try catch block to catch exceptions......hope this helps
The answer here is simple, you've forgotten exactly how events work, they are just like any other object, you have to instantiate them. aka.
public event ModuleCommunicationEventHandler ModuleCommunication = new ModuleCommunicationEventHandler(SomeStaticMethodThatWillBeCalledByDefault);

Attempt by method 'System.Web.Helpers.Json..cctor()' to access method 'System.Web.Helpers.Json.CreateSerializer()' failed

I am using System.Web.Helpers.Json to deserialize some JSON into dynamic in NET 4. The following line fails with this error: TypeInitializationException: Attempt by method 'System.Web.Helpers.Json..cctor()' to access method 'System.Web.Helpers.Json.CreateSerializer()' failed.
var json = Json.Decode(response);
The response is lengthy but valid JSON. What could be the matter here? I have tried LINQPad with a short handcrafted JSON and it worked. Is this a configuration issue of some sort?
[EDIT]
Here is the actual sample JSON. It appears the content is pretty much irrelevant. When this is run in a brand new Console application or LINQPad, it works as expected. But if you try to run the same code from a brand new Windows Forms application, it barfs with the above error.
var json = Json.Decode("{\"r\":{\"0\":{\"id\":\"2\"},\"1\":{\"id\":\"33\"}}}");
[EDIT2]
Actually, it turns out this has nothing to do with project types. The exception is thrown if the project is being debugged. If it is simply run, the exception does not occur. Strange, eh?
I forgot about this question and I found my answer in the meantime. I think it was somewhere on Microsoft's Connect site but I am not sure. So let's share it now.
Basically, in order to workaround this problem you need to make sure "Enable the Visual Studio hosting process" is unchecked in your project's settings under Debug. I am not sure why it's happening but this is definitely a way to "fix" it. I stopped searching for answers once I found out about this. It was good enough for me.
This can also happen if you are running in a partial trust.
Check the exception description here for possible reasons.
I don't know if this will apply to you, since you are not running in a web context, but this is what that link describes:
This exception is thrown in situations such as the following:
A private, protected, or internal method that would not be accessible from normal compiled code is accessed from partially
trusted code by using reflection.
A security-critical method is accessed from transparent code.
The access level of a method in a class library has changed, and one or more assemblies that reference the library have not been
recompiled.
There is problem in the inbuilt json class.
If you want to achieve this in alternate way, please use the below code:
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new DynamicJavaScriptConverter[] { new DynamicJavaScriptConverter() });
var result = WrapObject(serializer.DeserializeObject(value)); // here you will have result.
private object WrapObject(object value)
{
IDictionary<string, object> values = value as IDictionary<string, object>;
if (values != null)
{
return new DynamicJsonObject(values);
}
object[] arrayValues = value as object[];
if (arrayValues != null)
{
return new DynamicJsonArray(arrayValues);
}
return value;
}
Further to Roland's answer: some assembly mismatches listed can be fixed in the AssemblyInfo.cs file.
The offending line in my AssemblyInfo was this:
[assembly: AllowPartiallyTrustedCallers]
Removing this allowed me to access the public property (on a public class) that I was trying to set from another assembly that had dynamically loaded this assembly.

Handle Copy & Paste (Clipboard) in MVVM

If I want to access the clipboard with MVVM how can I do it?
While you certainly can do things like Clipboard.SetText and Clipboard.GetText in your VM, if you are an MVVM purist (like me), then I would recommend creating a ClipboardService (with an appropriate interface, so you can mock it in unit tests). Something like the following:
using System.Windows;
public class ClipboardService : IClipboardService
{
public void SetText(string value)
{
Clipboard.SetText(value);
}
public string GetText()
{
return Clipboard.GetText();
}
}
Then you can reference it as a property in your VM like so:
public IClipboardService ClipboardService { get; set; }
And either set it directly as a property or include it in your constructor:
public FooViewModel(IClipboardService service) {
ClipboardService = service;
}
And when you need it, instead of calling Clipboard.SetText directly, you can use ClipboardService.SetText instead. And you can (as mentioned before) mock it in unit tests. So, if you use Moq (like I do), you could have something like:
Mock<IClipboardService> clipMock = new Mock<IClipboardService>();
clipMock.Setup(mock => mock.GetText(It.IsAny<string>())).Returns("Foo");
And instantiate your VM like so:
var fooVm = new FooViewModel(clipMock.Object);
And so on.
I realize this is an ancient post, but I was looking for some best practices on Clipboards and MVVM, made my own decision while reading this post and decided to share. Hope somebody finds it useful. :-)
SL 4 now supports text clipboard operations. This is transparent in OOB mode and requires user confirmation if not in OOB mode.
You can use Clipboard.GetText() in your view models and commands to retrieve the text content available in the clipboard.

Categories

Resources