Serialization of derived FixedDocument - c#

Since you can only add pages to a FixedDocument, I wrote a derived class:
public class CustomFixedDocument : FixedDocument
{
public void RemoveChild(object child)
{
base.RemoveLogicalChild(child);
}
}
to replace FixedDocument, which works fine, until I try to print the document and receive the following error:
An unhandled exception of type
'System.Windows.Xps.XpsSerializationException' occurred in
ReachFramework.dll
Additional information: Serialization of this type of object is not
supported.
I haven't worked with serialization that much in the past and have read up on it but still can't solve the issues. I have also tried the
[Serializable]
attribute, but it doesn't make any difference.
Can anybody guide me in the correct direction or have any ideas what to do?

If you look at decompiled source code of the method which checks if certain type is supported, you will see roughly the following:
internal bool IsSerializedObjectTypeSupported(object serializedObject)
{
bool flag = false;
Type type = serializedObject.GetType();
if (this._isBatchMode)
{
if (typeof (Visual).IsAssignableFrom(type) && type != typeof (FixedPage))
flag = true;
}
else if (type == typeof (FixedDocumentSequence) || type == typeof (FixedDocument) || (type == typeof (FixedPage) || typeof (Visual).IsAssignableFrom(type)) || typeof (DocumentPaginator).IsAssignableFrom(type))
flag = true;
return flag;
}
Here you see that this type should either inherit DocumentPaginator, Visual, or be exactly of type FixedDocument, FixedDocumentSequence, FixedPage. So, types inherited from FixedDocument will not work, whatever serializable attributes you will use, so you have to find a different approach. I think that is a bug in XpsSerializationManager, but maybe there is some deep reason.

I decided to try the OP's approach and see if I can get it to work.
According to the snippet posted by Evk, although the IsSerializedObjectTypeSupported() function will not accept our own custom derivative of FixedDocument, it will accept a DocumentPaginator, and one of the overloads of XpsDocumentWriter.Write() accepts a paginator, so that should work, right?
Well, it turns out that if you do XpsDocumentWriter.Write( myFixedDocument.DocumentPaginator ) (where myFixedDocument is a custom derivative of FixedDocument) then something throws a NullReferenceException somewhere deep in library code. So, no luck there.
However, according to the same snippet, a FixedPage is also a supported type, and the XpsDocumentWriter.Write() method has another overload which accepts individual instances of FixedPage.
So, the following code worked for me:
foreach( FixedPage fixedPage in
fixedDocument.Pages.Select( pageContent => pageContent.Child ) )
xpsDocumentWriter.Write( fixedPage );
(Where Select() comes from using System.Linq)

Related

Text template custom host: how to implement ResolveDirectiveProcessor

Im trying to use this example from msdn on how to create a custom host for text template generation.
The CustomCmdLineHost class implements the ITextTemplatingEngineHost interface but not completely, the ResolveDirectiveProcessor is not implemented and it throws each time an exception wich is normal. Here is the ResolveDirectiveProcessor method:
public Type ResolveDirectiveProcessor(string processorName)
{
//This host will not resolve any specific processors.
//Check the processor name, and if it is the name of a processor the
//host wants to support, return the type of the processor.
//---------------------------------------------------------------------
if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
{
//return typeof();
}
//This can be customized to search specific paths for the file
//or to search the GAC
//If the directive processor cannot be found, throw an error.
throw new Exception("Directive Processor not found");
}
and processorName passsed to this function is "T4VSHost",
The question now:
What is the type of "T4VSHost" to return in this method ?
P.S.: i tried "Microsoft.Data.Entity.Design.VisualStudio.Directives.FallbackT4VSHostProcessor" but it seems that it doesnt exist in any namespace.
It appears that the only way is to create that type. how ? by creating a class (lets call it FallbackT4VSHostProcessor) that inherits from the DirectiveProcessor abstract class which lives in the Microsoft.VisualStudio.TextTemplating namespace (the only example i found in the internet is Here). then we need to return the type of FallbackT4VSHostProcessor in the ResolveDirectiveProcessor like this:
Type ITextTemplatingEngineHost.ResolveDirectiveProcessor(string processorName)
{
if (string.Compare(processorName, "T4VSHost", StringComparison.OrdinalIgnoreCase) == 0)
{
return typeof(FallbackT4VSHostProcessor);
}
throw new Exception("Directive Processor not found");
}
I hope this will help someone someday.

Using a Non-Const String as a CategoryAttribute Name

All, I am localising my C# application and I have come to my settings control. As I am using a PropertyGrid I am using properties to provide the options. I have a CategoryAttribute for "Server Access" I need to show in different languages - I had:
[CategoryAttribute("ServerAccess"),
ReadOnlyAttribute(false),
XmlElement,
DescriptionAttribute("Message about the Server Access Option.")]
public bool ShowSystemDatabases
{
get { return this.showSystemDatabases; }
set { this.showSystemDatabases = value; }
}
and I attempted to change this to:
[CategoryAttribute(ObjectStrings.SettingsServerAccess),
ReadOnlyAttribute(false),
XmlElement,
DescriptionAttribute(MessageStrings.SettingsShowSystemDatabaseInfo)]
public bool ShowSystemDatabases
{
get { return this.showSystemDatabases; }
set { this.showSystemDatabases = value; }
}
Where ObjectStrings is my resource file ('ObjectStrings.resx' et al.). This is throwing a compile-time error
An attribute argument must be a constant expression, typeof expression or array
creation expression of an attribute parameter type...
clearly I can't use a string when the constructor is expecting a const string. I have tried casting from string to const string using various round-the-houses method but all have failed. it is a simple enough issue, but...
What is the easiest way around this problem?
Thanks for your time.
I don't know how you could handle this especially in the case of control's attributes, but a general way to achieve this is to make Resources resolved at runtime, using parameters like these on your attribute :
MessageResourceType = typeof (MyResource), MessageResourceName = "MyResourceKey")
instead of directy passing the pointer to resource key.
I don't know if there is an equivalent way for CategoryAttribute, DescriptionAttribute and others Controls' properties attributes, or if there is a way to overload them.

Why do I have to manually create ExpandoObject to properly use the dynamic keyword?

I was looking at the question Use 'dynamic' throw a RuntimeBinderException. I face a similar problem:
Basically, I want to create a "HTML helper" in ASP.NET MVC that uses dynamic arguments, akin to the htmlArguments parameter for many of the existing helpers (more code below):
public BootstrapCell(Action<string> emitContentAction, dynamic args)
View:
#using (grid.Cell(ViewContext.Writer.Write, new {Position = 4}))
{
<p>zomg!</p>
}
However in the naive approach, i get RuntimeBinderException thrown at me, declaring that 'object' does not contain a definition for 'Position', even though when debugging and hovering over the _args variable, it clearly does have a Position property.
The caller and the callee are in separate assemblies. Why is that problem happening?
(The solution to that has been shown in the same question: Manually create an ExpandoObject to hold the args.)
Implementation:
public class Cell
{
private readonly string _tagName;
private dynamic _args;
private Action<string> EmitContentAction;
public BootstrapCell(Action<string> emitContentAction, dynamic args) : DisposableBaseClass
{
_args = args;
EmitContentAction = emitContentAction;
OnContextEnter();
}
protected void OnContextEnter()
{
var sb = new StringBuilder("<");
sb.Append(_tagName);
if (_args.Position > 0)
{
sb.Append(" class=\"offset");
sb.Append(args.Position);
sb.Append("\"");
}
sb.Append(">");
EmitContentAction(sb.ToString());
}
}
[Edited to make clearer that my problem arises when "obviously" the Position property is set. I am aware that if the property never was defined in the first place, an exception must be raised.]
That code is fatally flawed.
It does work, as long as you specify that property:
void Bar()
{
Foo(new {Position = 0});
}
void Foo(dynamic args)
{
Console.WriteLine(args.Position);
}
That will output 0, it will not throw a RuntimeBinderException.
But the purpose of such code is the possibility for the caller to specify only the properties needed and omit the rest.
You are trying to check for this omission via if(args.Position != null). But that doesn't work, it already requires Position to exist.
When you have a look at the routing API of ASP.NET that also supports those anonymous configuration objects you will notice that the type of the parameter is object and not dynamic.
Using object instead of dynamic will enable your API to be used across assembly boundaries.
So how does it work?
Just like in the linked answer, you need to manually create a dictionary of the properties. Whether you use a plain old Dictionary<string, object> or an ExpandoObject is a matter of preference.
Using ExpandoObject will make your code a bit simpler to read and write, but it is not required.
About the actual exception you are getting:
Please note that it tells you it can't find the Position property on object. If it would be an anonymous type that was missing the Position property the exception message wouldn't refer to object but to an anonymous type. Something like this:
'<>f__AnonymousType0' does not contain a definition for 'Position'

C# change Intellisense info

I have a member of some class that definition is Like:
public static object CallMethod(Type oType, String sMethodName, params object [] paramenters)
{
/* some code exist here, not required in this scenario */
CarrierContainer cc = new CarrierContainer();
CarrierContainer.SetMethod.SetServiceAndFunction(cc, oType.Name.Substring(1), sMethodName);
foreach (object item in paramenters) { cc.Params.Add(item); }
object obj = WCFClient.ExecuteService(cc);
return obj;
}
and my calling part is like:
return (ID)WCFServiceClient.CallMethod(ServiceType, "Save", this);
now my point is while I have completed type:
return (ID)WCFServiceClient.CallMethod(ServiceType, "Save",
i don't know how many parameters are required for "Save" and i want to show IntelliSense info while typing based on two parameters typed ServiceType, "Save".
Please anyone have suggestion about control IntelliSense info while typing some code in C# editor?
The explicit definition of the method claims:
I don't know exactly how many parameters can be in that array
So what you expect from the IntelliSense is not what defined in the code.
If you want IntelliSense help you, you should explicitly define parameters in function definition.
Other "solution" could be use of the comments in appropriate way.

Activator.CreateInstance can't find the constructor (MissingMethodException)

I have a class which has the following constructor
public DelayCompositeDesigner(DelayComposite CompositeObject)
{
InitializeComponent();
compositeObject = CompositeObject;
}
along with a default constructor with no parameters.
Next I'm trying to create an instance, but it only works without parameters:
var designer = Activator.CreateInstance(designerAttribute.Designer);
This works just fine, but if I want to pass parameters it does not:
var designer = Activator.CreateInstance(designerAttribute.Designer, new DelayComposite(4));
This results in an MissingMethodException:
Constructor voor type
Vialis.LightLink.Controller.Scenarios.Composites.DelayCompositeDesigner
was not found
Any ideas here?
The problem is I really need to pass an object during construction.
You see I have a designer which loads all the types that inherit from the CompositeBase. These are then added to a list from which the users can drag them to a designer. Upon doing so an instance of the dragged is added to the designer. Each of these classes have custom properties defined on them:
[CompositeMetaData("Delay","Sets the delay between commands",1)]
[CompositeDesigner(typeof(DelayCompositeDesigner))]
public class DelayComposite : CompositeBase
{
}
When the user selects an item in the designer, it looks at these attributes in order to load up a designer for that type. For example, in the case of the DelayComposite it would load up a user control which has a label and a slider which allow the user to set the "Delay" property of the DelayComposite instance.
So far this works fine if I don't pass any parameters to the constructor. The designer creates an instance of the DelayCompositeDesigner and assigns it to the content property of a WPF ContentPresenter.
But since that designer needs to modify the properties of the selected DelayComposite
in the designer, I have to pass this instance to it. That is why the constructor looks lie this:
public DelayCompositeDesigner(DelayComposite CompositeObject)
{
InitializeComponent();
compositeObject = CompositeObject;
}
Suggestions are welcome
#VolkerK
The result of your code is this:
<---- foo
Vialis.LightLink.Controller.Scenarios.Composites.DelayCompositeDesignerVoid
.ctor()
Vialis.LightLink.Controller.Scenarios.Composites.DelayCompositeDesignerVoid
.ctor(Vialis.LightLink.Controller.Scenarios.Composites.DelayComposite)
param:Vialis.LightLink.Controller.Scenarios.Composites.DelayComposite
foo ---->
Leppie, you were correct, I had for some reason referenced the Composites assembly in my UI application... which is not something I should have done as I was loading it at runtime. The following code works:
object composite = Activator.CreateInstance(item.CompositType,(byte)205);
var designer = Activator.CreateInstance(designerAttribute.Designer, composite);
As you can see the code does not have knowledge of the DelayComposite type.
This solves the current problem, but introduces many new ones for what I want to achieve,
either way thank you and thank you to everyone who has replied here.
As for the following code, suggested by multiple people:
var designer = Activator.CreateInstance(
designerAttribute.Designer,
new object[] { new DelayComposite(4) }
);
The Activator.CreateInstance has a signature that looks like this:
Activator.CreateInstance(Type type, params object[] obj)
So it should accept my code, but I will try the suggested code
UPDATE:
I've tried this as suggested:
var designer = Activator.CreateInstance(designerAttribute.Designer, new object[] { new DelayComposite(4)});
The result is the same.
I would think that your call would need to be:
var designer = Activator.CreateInstance(designerAttribute.Designer, new object[] { new DelayComposite(4) });
Unless, of course, it is that, in which case the answer is not immediately obvious.
I think you are dealing with a Type mismatch.
Likely the assembly is referenced in different places, or they are compiled against different versions.
I suggest you iterate through the ConstructorInfo's and do a paramtype == typeof(DelayComposite) on the appropriate parameter.
Though I hate printf-like debugging ...
public static void foo(Type t, params object[] p)
{
System.Diagnostics.Debug.WriteLine("<---- foo");
foreach(System.Reflection.ConstructorInfo ci in t.GetConstructors())
{
System.Diagnostics.Debug.WriteLine(t.FullName + ci.ToString());
}
foreach (object o in p)
{
System.Diagnostics.Debug.WriteLine("param:" + o.GetType().FullName);
}
System.Diagnostics.Debug.WriteLine("foo ---->");
}
// ...
foo(designerAttribute.Designer, new DelayComposite(4));
var designer = Activator.CreateInstance(designerAttribute.Designer, new DelayComposite(4));
What does that print in the visual studio's output window?
If you want to call this contructor...
public DelayCompositeDesigner(DelayComposite CompositeObject)
...just use this:
var designer = Activator.CreateInstance(typeof(DelayCompositeDesigner), new DelayComposite(4));
or
var designer = Activator.CreateInstance<DelayCompositeDesigner>(new DelayComposite(4));
I had a similar issue, however my problem was due to the visibility of the constructor. This stack overflow helped me:
Instantiating a constructor with parameters in an internal class with reflection
I discovered another way of creating an instance of an object without calling the constructor at all while answering another question on SF.
In the System.Runtime.Serialization namespace there is a function FormatterServices.GetUninitializedObject(type) that will create an object without calling constructor.
If you look at that function in Reflector you will see it is making an external call. I don't know how black magic is actually happening under the hood. But I did prove to myself that the constructor was never called but the object was instantiated.
When I encountered this problem, I was using a method that returned the parameter list to plug in to Activator.CreateInstance and it had a different number of arguments than the constructor of the object I was trying to create.
In my case, this code work good with .NET Framework but does not work in .NET Core 3.1. It throws ExecutionEngineException which is uncatchable. But when I change target to .NET 5, it works perfectly. Hope this help some one.
Type type = assembly.GetType(dllName + ".dll");
Activator.CreateInstance(type ), new Stream[] { stream };
You can use the following overload on CreateInstance:
public static Object CreateInstance(
Type type,
Object[] args
)
And in your case it'd be (I think):
var designer = Activator.CreateInstance(
typeof(DelayCompositeDesigner),
new object[] { new DelayComposite(4) }
);
I found a solution to the problem, I was struggling with the same issue.
Here is my activator:
private void LoadTask(FileInfo dll)
{
Assembly assembly = Assembly.LoadFrom(dll.FullName);
foreach (Type type in assembly.GetTypes())
{
var hasInterface = type.GetInterface("ITask") != null;
if (type.IsClass && hasInterface)
{
var instance = Activator.CreateInstance(type, _proxy, _context);
_tasks.Add(type.Name, (ITask)instance);
}
}
}
And here is my class to activate, note that I had to change the constructor params to objects, the only way I could get it to work.
public class CalculateDowntimeTask : Task<CalculateDowntimeTask>
{
public CalculateDowntimeTask(object proxy, object context) :
base((TaskServiceClient)proxy, (TaskDataDataContext)context) { }
public override void Execute()
{
LogMessage(new TaskMessage() { Message = "Testing" });
BroadcastMessage(new TaskMessage() { Message = "Testing" });
}
}

Categories

Resources