Reflection ok in 1.1, but not anymore in 2.0

F

Fred

Hi,

The code below works perfectly with Framework 1.1, but generates a run-time
error with the 2.0.
I use it to extend a PrintPreviewDialog to add my own PrintButton.

using System;
using System.Reflection;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

namespace PrintUtils
{

public class MyPrintPreviewDialog : System.Windows.Forms.PrintPreviewDialog
{

private ToolBarButton myPrintButton;

public MyPrintPreviewDialog() : base()
{

Type t = typeof(PrintPreviewDialog);
FieldInfo fi = t.GetField("toolBar1", BindingFlags.Instance |
BindingFlags.NonPublic);
FieldInfo fi2 = t.GetField("printButton", BindingFlags.Instance |
BindingFlags.NonPublic);

ToolBar toolBar1 = (ToolBar)fi.GetValue(this); <------ Error here :
"Object reference not set to in instance of an object"

ToolBarButton printButton = (ToolBarButton)fi2.GetValue(this);

printButton.Visible = false;

myPrintButton = new ToolBarButton();
myPrintButton.ToolTipText = printButton.ToolTipText;
myPrintButton.ImageIndex = 0;

ToolBarButton[] oldButtons = new ToolBarButton[toolBar1.Buttons.Count-1];
for(int i = 0 ; i < oldButtons.Length ; i++) oldButtons =
toolBar1.Buttons[i+1];

toolBar1.Buttons.Clear();
toolBar1.Buttons.Add(myPrintButton);

for(int i = 0 ; i < oldButtons.Length ; i++)
toolBar1.Buttons.Add(oldButtons);

toolBar1.ButtonClick += new
ToolBarButtonClickEventHandler(toolBar1_Click);

}

private void toolBar1_Click(object sender, ToolBarButtonClickEventArgs
eventargs)
{

if (eventargs.Button == myPrintButton)
{
PrintDialog printDialog1 = new PrintDialog();
printDialog1.Document = this.Document;
if (printDialog1.ShowDialog() == DialogResult.OK) this.Document.Print();
}
}

}
}

Thanks for helping.
 
J

Jon Skeet [C# MVP]

The code below works perfectly with Framework 1.1, but generates a run-time
error with the 2.0.

That's what you get for messing with encapsulation. By using
reflection to get at a private member variable, you were tying
yourself to an implementation. The implementation has changed in 2.0
(in a way which doesn't break encapsulation) so your code is broken.

Now, you could use Reflector to find out how the implementation has
changed, etc, but the lesson to learn here is that you're relying on
things you shouldn't rely on.

Jon
 
F

Fred

Well, so assuming i can't rely on such things anymore, what is the other way
(if there's one...) to do this ?!

Thanks again.
 
M

Marc Gravell

Well, so assuming i can't rely on such things anymore, what is the other way
(if there's one...) to do this ?!

Actually, even in 1.1 you couldl't *rely* on this - it could just have
easily broken in an 1.1 maintenance release.

At a push, you could maybe walk the Controls tree; for instance, there
is now a ToolStrip (called toolStrip1) which you could look at and
manipulate (which would be *marginally* better than using reflection,
but not much - you'd still be relying on knowledge from the inside of
the black box - it might allow you to remove the dependency on fields,
but you'd still be bound to specific Controls). However, the simple
answer is that there is no way to safely abuse encapsulation.
Sometimes controls deliberately expose hooks to provide a robust
mechanism to customise them, but not in this case - so anything you do
will be brittle.

Marc
 
F

Fred

Ok, i understand.
So, if i want a printpreviewdialog with a printdialog, wich solution should
i use ?

Thanks.
 
N

Nicholas Paldino [.NET/C# MVP]

Fred,

The solution here is to roll your own. If you insist on playing with
the internals of the PrintPreviewDialog, you will get burned any time of the
internal implementation details changes. By rolling your own (or waiting
until an appropriate extensibility point is added), you are not bound to
details which are being kept from you through encapsulation.
 
B

Bruce Wood

I understand Fred's frustration. PrintPreviewDialog continues to be
badly implemented in Windows Forms. It had no business being a sealed
black box in the first place, although at least they made
PrintPreviewControl a separate component that you can include in a
roll-your-own preview dialog.

I wanted to do something similar: override the behaviour of the Print
button. There is no "clean" way to do this: you must either build your
own dialog or abuse encapsulation on the existing one.

So, while I understand Fred's frustration with this one, Jon, Marc,
and Nicholas are correct: make your own dialog, or your code will
randomly break every time MS tweaks the PrintPreviewDialog.

Why, while they were in there messing with the tool bar / tool strip,
they didn't add a few more capabilities to the control... well, I
guess that would have been a lot more work to make it upwardly
compatible.

Then again, here's another idea: look into WPF and see if things
haven't gotten better in that world, at least so far as print preview
dialog is concerned.

Fred,

The solution here is to roll your own. If you insist on playing with
the internals of the PrintPreviewDialog, you will get burned any time of the
internal implementation details changes. By rolling your own (or waiting
until an appropriate extensibility point is added), you are not bound to
details which are being kept from you through encapsulation.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)




Ok, i understand.
So, if i want a printpreviewdialog with a printdialog, wich solution
should i use ?
 
Top