C# How to replace an Event by a Delegate? - c#

Mac OS, VS Community, C#, Cocoa application.
The following code generates a run time error.
using AppKit;
using CoreGraphics;
using Foundation;
using System;
namespace NSTextFieldValidation
{
public partial class ViewController : NSViewController
{
public ViewController(IntPtr handle) : base(handle) { }
public override void ViewDidLoad()
{
base.ViewDidLoad();
var fm = new NSNumberFormatter { Maximum = 999, Minimum = 0 };
NSTextField Tbx1 = new NSTextField(new CGRect(10, 50, 100, 30));
View.AddSubview(Tbx1);
Tbx1.Formatter = fm;
Tbx1.Delegate = new MyTextFieldDelegate();
Tbx1.DidFailToValidatePartialString += TbxDidFailToValidatePartialString;
}
private void TbxDidFailToValidatePartialString(object sender, NSControlTextErrorEventArgs e)
{
Console.WriteLine("DidFailToValidatePartialString activated");
}
}
public class MyTextFieldDelegate : NSTextFieldDelegate
{
override public void EditingEnded(NSNotification notification)
{
Console.WriteLine("EditingEnded");
}
}
}
Here's the exception:
System.InvalidOperationException
Event registration is overwriting existing delegate. Either just use
events or your own delegate: NSTextFieldValidation.MyTextFieldDelegate
AppKit.NSTextField+_NSTextFieldDelegate
I understand that it is not correct to use event and delegate at the same time. I only ask which method I need to override in my delegate to deal with "DidFailToValidatePartialString", because I did not find it (perhaps I did not look well).

In the Delegate class, the DidFailToValidatePartialString method is not declared like others (i.e EditingEnded). We need to add an Export attribute:
[Export("control:didFailToValidatePartialString:errorDescription:")]
I would like the Xamarin documentation to explain this.

Related

Routine call in other namespace

I wanted to use a DLL library code and link / reference it with my c# code. The reference seems to be working, when defining var instance = new ProjectM1() without any problems.
But in the following line appears an error that ProjectM1 does not contain a definition for ProjectM1 and it couldn't find a extension method (Maybe a using directive or assembly reference is missing)
namespace GanttC
{
public class ProjectM1 : ProjectM1<Task, object>
{
}
public class ProjectM1<T, R> : IProjectM1<T, R>
where T : Task
where R : class
{
public ProjectM1() (routine to call !)
{
Now = 0;
Start = DateTime.Now;
TimeScale = GanttC.TimeScale.Day;
}
}
}
Here now the main code to call routine ProjectM1 in public class ProjectM1:
using GanttC
namespace WindowsApp2
{
public partial class Form4 : Form
{
Form2 fh;
public Form4(Form2 caller)
{
fh = caller;
InitializeComponent();
}
private void Form4_Load(object sender, EventArgs e)
{
var instance = new ProjectM1();
instance.ProjectM1(); (error !!!)
}
}
}

Delegates and setting labels

So, my overall goal with this code is to set the text property of labels, from a different thread (in a safe manner).
namespace csDinger3
{
public delegate void setlblStarted_txt(string text);
public partial class ClientUI : Form
{
public void setlblStarted_txt(string text)
{
var setlblStarted a = new setlblStarted(setlblStarted_txt);
if (this.lblStarted.InvokeRequired)
{
this.Invoke(a, new object[] { text });
}
else
{
this.lblStarted.Text = text;
}
}
}
}
Calling code:
namespace csDinger3
{
public class Program
{
// Some code that's not relevant
public static void updateText(Int32 number)
{
setlblStarted x = new setlblStarted(ClientUI.setlblStarted_txt);
x(number.ToString());
}
}
}
From what I can understand (and please correct me if I'm wrong), I need to create a new instance of setlblStarted_txt, point that new instance at method setlblStarted_txt, but the issue is currently ClientUI.setlblStarted_txt isn't static, and wants an object reference.
I've tried using ClientUI c = new ClientUI();, but that doesn't work (because it's creating a new instance of the form?)
What am I doing wrong, and if possible, can you help me understand why?
In .Net 4.0, you can use actions:
if (InvokeRequired)
{
Invoke(new Action<string>(updateText), "some text");
}
else
{
updateText("some text");
}
Also, void updateText(string text) does not need to be static.
As I understand, you are trying to use MethodInvoker delegate to update your text. I suggest you to change this approach to simplify your code:
namespace csDinger3
{
public class Program
{
static ClientUI aForm;
static void Main()
{
aForm = new ClientUI();
aForm.Show();
}
// Some code that's not relevant
public static void updateText(Int32 number)
{
aForm.setlblStarted_txt(number.ToString());
}
public partial class ClientUI : Form
{
public void setlblStarted_txt(string text)
{
if (lblStarted.InvokeRequired)
{
Invoke(new EventHandler(delegate
{
lblStarted.Text = text
}));
}
else
{
lblStarted.Text = text;
}
}
You can achieve the same behaviour with using the ThreadPool or SynchronizationContext or Dispatcher (in WPF). Please see this tutorial for better understanding:
Beginners Guide to Threading in .NET: Part 5 of n
Understanding SynchronizationContext (Part I)
It's All About the SynchronizationContext

RectForMapRect MonoTouch.UIKit.UIKitThreadAccessException

I have this issue only in MonoTouch 5.4. DrawMapRect is multithreaded, but I need to use RectForMapRect here. Can I do it without InvokeOnMainThread?
Error:
MonoTouch.UIKit.UIKitThreadAccessException: UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
at MonoTouch.UIKit.UIApplication.EnsureUIThread () [0x00019] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:49
at MonoTouch.MapKit.MKOverlayView.RectForMapRect (MKMapRect mapRect) [0x00000] in /Developer/MonoTouch/Source/monotouch/src/MapKit/MKOverlayView.g.cs:146
at MapTest.MyMKOverlayView.DrawMapRect (MKMapRect mapRect, Single zoomScale, MonoTouch.CoreGraphics.CGContext context) [0x00009] in /Users/kirill/Desktop/MapTest/MapTest/MapTestViewController.cs:38
Source code here:
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.MapKit;
namespace MapTest
{
public class MyMKMapView : MKMapView
{
public MyMKMapView(RectangleF frame) : base(frame)
{
GetViewForOverlay = GetViewForOverlayImp;
}
private MKOverlayView GetViewForOverlayImp(MKMapView mapView, NSObject overlay)
{
return new MyMKOverlayView();
}
public void AddNewOverlay()
{
AddOverlay(new MyMKOverlay());
}
}
public class MyMKOverlayView : MKOverlayView
{
public MyMKOverlayView() : base()
{
}
public override void DrawMapRect(MKMapRect mapRect, float zoomScale, MonoTouch.CoreGraphics.CGContext context)
{
base.DrawMapRect(mapRect, zoomScale, context);
RectForMapRect(new MKMapRect());
}
}
public class MyMKOverlay : MKOverlay
{
public override MKMapRect BoundingMapRect
{
get
{
return new MKMapRect(10 , 10 , 10 , 10);
}
}
public MyMKOverlay() : base()
{
}
}
public partial class MapTestViewController : UIViewController
{
private MyMKMapView _map;
public MapTestViewController() : base ("MapTestViewController", null)
{
_map = new MyMKMapView(View.Bounds);
View.AddSubview(_map);
}
public override void DidReceiveMemoryWarning()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidUnload()
{
base.ViewDidUnload();
// Clear any references to subviews of the main view in order to
// allow the Garbage Collector to collect them sooner.
//
// e.g. myOutlet.Dispose (); myOutlet = null;
ReleaseDesignerOutlets();
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
_map.AddNewOverlay();
}
public override bool ShouldAutorotateToInterfaceOrientation(UIInterfaceOrientation toInterfaceOrientation)
{
// Return true for supported orientations
return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
}
}
}
Error in line RectForMapRect(new MKMapRect()).
This check is a new feature of MonoTouch 5.4. By default the UI thread check is only enabled on DEBUG builds. You can, at build-time, manually:
disable it (on DEBUG) using --disable-thread-check; or
enable it (on Release) using --force-thread-check;
You can also turn it on/off at runtime using CheckForIllegalCrossThreadCalls.
It's possible that the check is not required, Apple documentation on thread safety is not very clear (unlike MSDN). If you find such case (or simply are not sure) then please fill a bug report.

Returning a String from a C# WinForm .dll

I have a C# .dll that is envoked from within a C# application useing "System.Reflection" at runtime. The .dll contains a WinForm class which is used to display information to the user. The .dll is envoked using the following code:
DLL = Assembly.LoadFrom(strDllPath);
classType = DLL.GetType(String.Format("{0}.{0}", strNsCn));
classInst = Activator.CreateInstance(classType, paramObj);
Form dllWinForm = (Form)classInst;
dllWinForm.ShowDialog();
Now, my problem is now that I want to return a string from the WinForm .dll. This could be an error or just to show that the process completed correctly. I know how this is done when calling a method from within a requested .dll, as follows:
System.Reflection.Assembly LoadedAssembly = System.Reflection.Assembly.Load("mscorlib.dll");
System.Console.WriteLine(LoadedAssembly.GetName());
object myObject = LoadedAssembly.CreateInstance("System.DateTime", false, BindingFlags.ExactBinding, null, new Object[] {2000, 1, 1, 12, 0, 0}, null, null);
MethodInfo m = LoadedAssembly.GetType("System.DateTime").GetMethod("ToLongDateString");
string result = (string) m.Invoke(myObject, null);
but how do you do this for my case of a WinForm called from a .dll at runtime?
Any suggestions would be most appreciated.
Okay, so to boil the question and the comments down, what we're trying to do here is have a C# app that loads a dll that was implemented by a 3rd party at a later date, and the app needs to get some status information from the component in the loaded dll (the fact that the component uses WinForms vs. some other UI seems completely inconsequential).
The best way to do it is to start out with an interface or base class that can be shared between the hosting application and the loaded component. In order to achieve this, the interface needs to be in a separate dll. So first we create a class library project and add the following class:
using System;
using System.Windows.Forms;
namespace SimplePluginShared
{
public class PluginBase : Form
{
public virtual String GetStatus()
{
return null;
}
}
}
Then add a reference to that class library from the project that implements the component you're loading via reflection (or share it with your third party for them to implement). Here is an example implementation of Plugin Base:
using System;
using System.Windows.Forms;
using SimplePluginShared;
namespace SimplePluginExample
{
public partial class MyForm : PluginBase
{
private String _status = "Unspecified";
public MyForm()
{
InitializeComponent();
}
public override string GetStatus()
{
return _status;
}
private void btnGive_Click(Object sender, EventArgs e)
{
_status = "Give Him The Stick.";
this.DialogResult = DialogResult.OK;
this.Close();
}
private void btnDontGive_Click(object sender, EventArgs e)
{
_status = "Don't Give Him The Stick!";
this.DialogResult = DialogResult.Cancel;
this.Close();
}
}
}
And lastly the code to load and call the component:
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using SimplePluginShared;
namespace SimplePluginHost
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnBrowse_Click(Object sender, EventArgs e)
{
OpenFileDialog openPluginDlg = new OpenFileDialog() { DefaultExt = "dll", Multiselect = false, Title = "Open Plugin DLL", Filter = "DLLs|*.dll" };
if (openPluginDlg.ShowDialog() == DialogResult.OK)
{
txtPluginPath.Text = openPluginDlg.FileName;
}
}
private void btnGo_Click(Object sender, EventArgs e)
{
Assembly pluginDll = Assembly.LoadFrom(txtPluginPath.Text);
Type pluginType = pluginDll.GetTypes().Where(t => typeof(PluginBase).IsAssignableFrom(t)).First();
PluginBase pluginInstance = (PluginBase)Activator.CreateInstance(pluginType);
pluginInstance.ShowDialog();
MessageBox.Show(pluginInstance.GetStatus());
}
}
}
He are some screenshots:
Why can't you add the dll as a reference to your project and call it that way? (Just like any other assembly?)

MEF. How to load a winform into winform container?

I have decided to play a little bit with MEF2 and net3.5 and I have thought it would be easy but I am stuck now. Generally the idea of my toy is I want to have form containet where I am going to load form extensions and show them. I did this code
My extension:
using System.ComponentModel.Composition;
using System.Windows.Forms;
namespace MyExtantion
{
public interface IForm
{
void LoadForm(Form form);
}
[Export(typeof(IForm))]
public partial class MyExtantion : Form, IForm
{
public MyExtantion()
{
InitializeComponent();
}
public void LoadForm(Form form)
{
MdiParent = form;
Show();
}
}
}
and form container
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Windows.Forms;
namespace FormsContainer
{
public partial class FormContainer : Form
{
public FormContainer()
{
InitializeComponent();
}
private CompositionContainer _container;
public interface IForm
{
void LoadForm(Form form);
}
[Import(typeof(IForm))]
public IEnumerable Forms { get; set; }
private bool Compose()
{
var catalog = new AggregateCatalog(
new AssemblyCatalog(Assembly.GetExecutingAssembly()),
new DirectoryCatalog("Extantions"));
var batch = new CompositionBatch();
batch.AddPart(this);
_container = new CompositionContainer(catalog);
try
{
_container.Compose(batch);
}
catch (CompositionException compositionException)
{
MessageBox.Show(compositionException.ToString());
return false;
}
return true;
}
private void FormContainer_Load(object sender, EventArgs e)
{
if (Compose())
foreach (IForm form in Forms)
{
form.LoadForm(this);
}
}
}
}
The problem is I can not load my extantion and I have this error
{"The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.\r\n\r\n1) No exports were found that match the constraint '((exportDefinition.ContractName = \"FormsContainer.FormContainer+IForm\") && (exportDefinition.Metadata.ContainsKey(\"ExportTypeIdentity\") && \"FormsContainer.FormContainer+IForm\".Equals(exportDefinition.Metadata.get_Item(\"ExportTypeIdentity\"))))'.\r\n\r\nResulting in: Cannot set import 'FormsContainer.FormContainer.Forms (ContractName=\"FormsContainer.FormContainer+IForm\")' on part 'FormsContainer.FormContainer'.\r\nElement: FormsContainer.FormContainer.Forms (ContractName=\"FormsContainer.FormContainer+IForm\") --> FormsContainer.FormContainer\r\n"}
How I can achieve it with MEF? and What I do wrong?
You are declaring the IForm interface in two different places.
If you only reference one interface that both are using this code works properly.

Categories