|
Posted
over 16 years
ago
by
MattWard
Python auto-indentation has now been added to
SharpDevelop 3.1 in revision 5007. The latest SharpDevelop
builds can be downloaded from the
build
server.
The indentation will be increased after a line ending with the
... [More]
colon character, such as a method declaration. After typing in a
pass or return statement the indentation will be decreased on the
following line.
[Less]
|
|
Posted
over 16 years
ago
by
MattWard
Getting an IronPython WinForm to display an icon or image can be
done in different ways. Here we will take a look at the following
ways to add a background image to the main form of an IronPython
WinForms application.
Using the
... [More]
SharpDevelop forms designer.
Loading an image embedded as a resource in a separate
assembly.
Loading an image from disk.
Using the SharpDevelop Forms Designer
The SharpDevelop forms designer can be used to add resources to
a form in the same way for other languages such as C# or VB.NET. It
supports adding local form resources but not project resources
currently.
After
creating a new IronPython Windows Application, open the
MainForm in the designer. In the Properties window select the
BackgroundImage property of the MainForm and click the browse
button.
Select Local Resource then click the Import button to browse to
the image file you are going to use.
Click OK to close the dialog. The background image should then
be displayed in the form. If you select Run from the Debug menu
your application will be compiled and the main form should be
displayed with your image.
Now let us take a look at the code generated for the form.
import System.Drawing import System.Windows.Forms from System.Drawing import * from System.Windows.Forms import * class MainForm(Form): def __init__(self): self.InitializeComponent() def InitializeComponent(self): resources = System.Resources.ResourceManager("PythonWinApp.MainForm", System.Reflection.Assembly.GetEntryAssembly()) self.SuspendLayout() # # MainForm # self.BackgroundImage = resources.GetObject("$this.BackgroundImage") self.ClientSize = System.Drawing.Size(284, 264) self.Name = "MainForm" self.ResumeLayout(False)
The generated code for the InitializeComponent method is nearly
the same as the code generated by the forms designer when adding a
resource to a C# form. The generated C# code is shown below.
private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); this.SuspendLayout(); // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage"))); this.ClientSize = new System.Drawing.Size(284, 264); this.Name = "MainForm"; this.Text = "WinApp"; this.ResumeLayout(false); }
The C# form uses the ComponentResourceManager class and
typeof(MainForm) to get access to the embedded resource. The
IronPython form uses the ResourceManager class instead. If we try
to use a ComponentResourceManager in the IronPython application by
replacing the resources line with:
resources = System.ComponentModel.ComponentResourceManager(clr.GetClrType(MainForm))
The application will throw an NotSupportedException when it is
run with an error message saying that "The invoked member is
not supported in a dynamic assembly.". The line of code that
actually causes this error is the resources.GetObject() line. So we
cannot read a resource from a dynamic assembly, but even if we
could the resources are not actually embedded in the assembly that
contains the code for the MainForm. When the IronPython application
is compiled two files are generated an executable and a dll. The
dll is generated using IronPython's ClrModule.CompileModules
which is used to compile all the IronPython code into an assembly.
The exe is generated using several Reflection.Emit calls and its
only task is to call PythonOps.InitializeModule passing the
filename of the generated dll. If you look at the exe using
Reflector you can see a PythonMain class with a Main method similar
to that shown below.
[STAThread] public static int Main() { string[] references = new string[] { "IronPython, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" }; return PythonOps.InitializeModule(Assembly.LoadFile(Path.GetFullPath("PythonWinApp.dll")), "Program", references); }
With Reflector you can also see that the form resources are
embedded into the executable and not the dll. So this means our
form code needs to load the resources from the executable. This is
done in the generated form designer code by passing the entry
assembly, which will be the executable containing the resources, to
the ResourceManager as shown below.
resources = System.Resources.ResourceManager("PythonWinApp.MainForm", System.Reflection.Assembly.GetEntryAssembly())
The above code will not work if you try to run your application
with the IronPython Console (ipy.exe) since the entry assembly will
actually be ipy.exe. The following sections will look at using form
resources that can be used when running your code with ipy.exe.
Loading an Image from an Assembly
Here we will have two projects, one with the IronPython form and
one with the image resource. In the resource project you have a
choice of whether you are going to put your image inside a resource
file (.resx) or simply add it to the project and set its Build
Action to EmbeddedResource. For now let us add the image to a
resource file. Add a new resource file and then open it into the
editor. Add your image to the resource file by right clicking and
selecting Add files...
Finally compile your resource assembly.
From your IronPython WinForms project you can load the image
from this resource assembly with code similar to the following:
class MainForm(Form): def __init__(self): self.InitializeComponent() fileName = System.IO.Path.GetFullPath("Resources\\ResourceLibrary.dll") assembly = System.Reflection.Assembly.LoadFile(fileName) resources = System.Resources.ResourceManager("ResourceLibrary.Resources", assembly) self.BackgroundImage = resources.GetObject("SharpDevelop") def InitializeComponent(self): pass
The code loads the resource assembly (ResourceLibrary.dll) by
assuming it is in the Resources subfolder off the current working
directory. The name of the embedded resource and the loaded
assembly are then passed to the ResourceManager. Finally the
SharpDevelop image is read from the resource manager via the
GetObject method. This code will work with both ipy.exe and when
the application is compiled but it uses the current working
directory is used to determine the location of the resource
assembly. An improvement is to use a path relative to the
MainForm.py file itself.
class MainForm(Form): def __init__(self): self.InitializeComponent() directoryName = System.IO.Path.GetDirectoryName(__file__) fileName = System.IO.Path.Combine(directoryName, "Resources\ResourceLibrary.dll") assembly = System.Reflection.Assembly.LoadFile(fileName) resources = System.Resources.ResourceManager("ResourceLibrary.Resources", assembly) self.BackgroundImage = resources.GetObject("SharpDevelop") def InitializeComponent(self): pass
With this modified code we get the full filename including its
path to the MainForm.py file by using the __file__ constant. Then
we can get the full path to the resource assembly relative to the
MainForm.py file. This code will work with ipy.exe but not if the
application is compiled. With the compiled application the __file__
constant returns the name of the file without its extension and
without any path information.
Loading an Image from Disk
Here we assume the image (SharpDevelop.png) is in a Resources
subfolder relative to the MainForm.py file.
class MainForm(Form): def __init__(self): self.InitializeComponent() directoryName = System.IO.Path.GetDirectoryName(__file__) fileName = System.IO.Path.Combine(directoryName, "Resources\\SharpDevelop.png") self.BackgroundImage = System.Drawing.Bitmap(fileName) def InitializeComponent(self): pass
Here the code simply determines the full path to the image and
then loads it into a Bitmap. The Bitmap is then used to set the
form's background image. Again this code will work with
ipy.exe but not if the application is compiled.
Example code for all the ways of using resources in IronPython
can be found in the
IronPythonFormResourceExamples.zip file. [Less]
|
|
Posted
over 16 years
ago
by
martinkonicek
During SharpDevelop developer days 2009 we implemented new debugger features to appear in SharpDevelop 4:
Debugger tooltips for IEnumerable
The individual items of IEnumerable can be expanded normally.
Text visualizer
There is a new
... [More]
visualizer chooser in debugger tooltips.
For string values, Text and XML visualizers are available. Choosing "Text visualizer" shows a window:
XML visualizer
XML visualizer uses AvalonEdit to display syntax-highlighted xml. Folding (= node expanding/collapsing) and auto indentation will be implemented.
[Less]
|
|
Posted
over 16 years
ago
by
siegi44
Although we had basic support for profiling unit tests in the RC1 of SharpDevelop 3, it was very hard to use. The reason for this was that the profiler simply attached itself to the NUnit console runner used by SharpDevelop and recorded all calls
... [More]
, even the initialization of NUnit. This produced a large overhead of data.
In SharpDevelop 3.1 rev. 4863 I changed the data processor a bit. The profiler now displays all unit tests as root nodes in the "Overview" tab and all NUnit initialization calls are removed from the data.
As you can see, it is much easier find the information you need. This also works with multiple tests selected.
Another feature I thought it could be useful for profiling in general is "Find references". Simply right-click on a node in the tree view and select "Find references" from the context menu.
I hope you like the changes and find it useful. If you have any questions or suggestions, please post them here.
[Less]
|
|
Posted
over 16 years
ago
by
siegi44
Important note: The changes discussed in this post apply only to SharpDevelop 4.0.0.4537 and later revisions.
Why?
As you might have noticed, I am one of the accepted Google Summer of Code 2009 students. While working on my project, the XAML
... [More]
binding, I had the idea of implementing an Outline Pad for XAML. After a short discussion with my mentor, we decided to add a general solution for implementing this functionality for C#, VB .NET, Boo, Windows Forms Designer and the (upcoming) WPF Designer too.
For instance, normal backend bindings like the C#, VB .NET and Boo binding could display a tree of the current document structure with namespaces, classes, member variables, methods properties, etc. This would allow fast physical restructuring of a file. With XAML you could easily move a part of the XAML tree to another location. Same would go for the Windows Forms and WPF Designer.
So each language binding or display binding should be able to tell the outline pad what content it should display to the user. The existing ILanguageBinding only represented a language from the point of view of the project system, there was no support for things like code completion, formatting or highlighting. So I changed the name of the existing ILanguageBinding to "IProjectBinding".
The New Language Binding API
The new ILanguageBinding is not project-based, but file- or, to be more specific, ITextEditor-based. What does this mean? It simply means, that every ITextEditor instance (that is, every file opened by the default text editor in SharpDevelop), gets its own ILanguageBinding instance. Furthermore, if you use the split-view functionality present in SharpDevelop, both views get their own ILanguageBinding instance too. Please keep that in mind, when implementing your own language bindings.
So let's move on the practical part: In fact, the "API" only consists of one interface, plus wrappers for the SharpDevelop addin system, to make it easy for backend binding addins to provide their own language-specific implementation. We will discuss its structure first:
public interface ILanguageBinding{
IFormattingStrategy FormattingStrategy {
get;
}
LanguageProperties Properties {
get;
}
void Attach(ITextEditor editor);
void Detach();}
It is not very complex, but there are a few things to keep in mind:
Attach is called only once on every ILanguageBinding instance (before any other method calls).
SharpDevelop will never Detach() and re-Attach() a language binding; instead, it will detach the old binding and attach a new instance. A new instance is created in the following situations:
an ITextEditor is created by SharpDevelop,
the filename, of the file being edited, has changed
or the user switches to split-view mode.
You can attach additional functionality like a custom highlighter or an IOutlineContentHost, to provide content displayed in the Outline pad, as mentioned at the beginning.
If you override Attach, you should also override Detach. It is called only once in the life-cycle of an ILanguageBinding instance:
when an ITextEditor is closed (disposed by SharpDevelop),
before a new language binding is attached, after the filename, of the file being edited, has changed. (So SharpDevelop disposes the old one before creating a new one.)
after switching from split-view to normal view mode, to dispose the second view, as it is not needed anymore.
You should detach (and dispose) all services and additional functionality, added by your language binding, from the ITextEditor at this point.
FormattingStrategy should return an instance of the IFormattingStrategy implementation for your language binding.
Properties should return a reference to the LanguageProperties implementation for your language binding.
Example: XamlLanguageBinding
I pasted the Attach and Detach method from the XamlLanguageBinding as a small example. Please note that adding custom syntax highlighting (in this case: XamlColorizer) and additional services, like IOutlineContentHost, requires a reference to the TextView of the AvalonEdit.TextEditor class. So this part can only be implemented for specific text editor controls.
public override void Attach(ITextEditor editor)
{
base.Attach(editor);
// try to access the ICSharpCode.AvalonEdit.Rendering.TextView
// of this ITextEditor
this.textView = editor.GetService(typeof(TextView)) as TextView;
// if editor is not an AvalonEdit.TextEditor
// GetService returns null
if (textView != null) {
colorizer = new XamlColorizer(editor, textView);
// attach the colorizer
textView.LineTransformers.Add(colorizer);
// add the XamlOutlineContentHost, which manages the tree view
textView.Services.AddService(typeof(IOutlineContentHost), new XamlOutlineContentHost(editor));
}
}
public override void Detach()
{
base.Detach();
// if we added something before
if (textView != null && colorizer != null) {
// remove and dispose everything we added
textView.LineTransformers.Remove(colorizer);
textView.Services.RemoveService(typeof(IOutlineContentHost));
colorizer.Dispose();
}
}
Nice, But How Do I Register My Language Binding With SharpDevelop?
Similar to all other parts of SharpDevelop, just add it to the addin path /SharpDevelop/Workbench/LanguageBindings like this:
<Path name="/SharpDevelop/Workbench/LanguageBindings">
<LanguageBinding
id="XAML"
class="ICSharpCode.XamlBinding.XamlLanguageBinding"
extensions=".xaml" />
</Path>
short explanation:
id: The name of the LanguageBinding, must be unique, preferably the same as your project binding or code completion binding.
class: The fully-qualified name of your language binding class.
extensions: A semicolon-separated list of file extensions, that are handled by your language binding. Please note that multiple LanguageBindings can handle a file extension. But only the first non-null FormattingStrategy is used or only the first non-null LanguageProperties instance is used. You can control the order of the language bindings using the insertbefore and insertafter attributes.
Important: In the past you were able to register a formatting strategy by adding it to /AddIns/DefaultTextEditor/Formatter/, this was removed. Now the IFormattingStrategy gets set by overriding the FormattingStrategy property in the Language Binding implementation.
How To Access The Features From AddIns?
When dealing with text displayed inside a SharpDevelop text editor, the easiest way to do this is to use the ITextEditor interface. The active language binding can be accessed by using ITextEditor.Language property.
Project Bindings
I have not done any changes to the project binding API, except renaming everything. Project bindings are responsible for project management (reading project files, compilation management, etc.). XAML does not need its own project format (there is no .xamlproj ;-)), so I cannot provide an example and explain it to you, please take a look at existing project bindings such as CSharpProjectBinding, VBNetProjectBinding or BooProjectBinding.
Conclusion
I think the language bindings make it easier to extend ITextEditor with language specific features and also allow other AddIns to access these features. In my next post I will explain my work on the XAML binding more in detail and guide you through the features I implemented.
If you have any questions concerning language bindings and these changes, please feel free to ask. [Less]
|
|
Posted
over 16 years
ago
by
martinkonicek
Hi,
my name is Martin Konicek and starting this summer, I will be trying to make your debugging experience in SharpDevelop even better. This blog will be about my project for Google summer of code 2009 - Debugger visualizers. The exciting part is
... [More]
that I'll be working on features that are not present in any IDE today.
Motivation
The idea is to introduce new ways to let you observe the state of the program during debugging. For me, by far the most used debugger feature are the tooltips. The problem with tooltips is they never show you the data structure "as a whole". Here we have two objects having a reference to each other:
As we can see, the tooltips can be expanded forever, even if we have only two objects forming a loop - there is no easy was to tell this information just from the tooltips. Long ago, I was thinking how gain more insight - what about displaying the objects in memory and references between them as an oriented graph, updating live when stepping in the debugger? I wrote it as a hobby project, which gave me important lessons how not to do that.
Object graph visualizer
I found out that SharpDevelop team had similar idea for Google summer of code, great! I applied, researched and wrote a prototype, which I sent to David Srbecky, the author of SharpDevelop debugger and my mentor. The prototype looked like this:
In the picture, the visualizer (right) shows all objects that can be reached from variable "a" in the program being debugged (left).
There is one more visualizer that is totally missing in today's
IDEs in my opinion, and will be implemented. About that, next time.
Bio
I'm 23, from the Czech Republic, currently studying CS at Charles University in the beautiful city of Prague. I think .NET is great, I have used it as my primary platform for a couple of years now. I'm teaching basics of programming to freshmen at our university - I recommend this to everyone, it's a great experience. I have a personal programming blog here.
Stay tuned for updates.. [Less]
|
|
Posted
almost 17 years
ago
by
MattWard
With
SharpDevelop 3.1 you can now debug IronPython code with the
IronPython Interpreter (ipy.exe).
Before you start make sure the debugger is set to use the
Just My Code feature. From the Tools menu select Options and
then
... [More]
click the Debugging category.
Ensure that the Just My Code feature is checked and that
the Step over code without symbols is not checked. If the
Step over code without symbols option is selected then stepping
will not work properly and lines of code will be skipped over.
There are two ways to debug your code. You can use the Python
menu or modify the project options. We will look at both of these
alternatives. First open your IronPython project into SharpDevelop.
Open your main file and make sure it is the active text editor
window. Set a breakpoint somewhere in your code. Then from the
Python menu select Run.
This will start ipy.exe which will run your code and the
debugger should stop the execution at the breakpoint.
From this point you can do the usual debugging activities such
as stepping through your code, viewing the callstack, adding items
to the watch window, etc.
If you want to use a different ipy.exe then this can be
specified in the Python Options dialog (Tools menu | Options).
To enable debugging when you press F5 or select the Debug Run
menu option you can modify the project options. From the Projects
menu select Project Options and then open the Debug tab. Here you
should change the Start Action to Start external program and
use the browse button to locate ipy.exe. In the Start Options add
the following command line arguments, changing the name of your
main file as required.
-D ${ProjectDir}\Program.py
Once these changes are saved you can then press F5 and ipy.exe
will be run under the debugger instead of running the compiled
executable.3
Issues
No support for debugging the executable produced by the
IronPython compiler since it does not produce debug symbols (i.e.
.pdb files).
When using ipy.exe you need to add references to .NET
assemblies explicitly in your code except for System which is
included by default. For example:
import clr clr.AddReference("System.Windows.Forms")
Thanks
Thanks to
David
Srbecky, SharpDevelop's debugger expert and maintainer,
for reviewing the code changes I wanted to make to the debugger and
making sure nothing was broken. Adding support for debugging
IronPython was straightforward and required 10-15 lines of new code
thanks to the code already written by David.
Thanks also to Harry Pierson
(IronPython Program Manager at Microsoft) who has written a great
set of blog posts on
creating an IronPython debugger in IronPython which gave me the
reason why SharpDevelop's debugger was not working when
debugging IronPython code. [Less]
|
|
Posted
almost 17 years
ago
by
MattWard
Support for designing Windows Forms in IronPython is now
available in SharpDevelop 3.1. The
original IronPython forms designer was removed when
SharpDevelop 3.0 began supporting
IronPython 2.0
which had removed
... [More]
support for generating IronPython code from
Microsoft's CodeDOM. The forms designer has now been
re-implemented to use the IronPython
abstract syntax tree (AST) and no longer relies on the
CodeDOM.
Creating a Windows Application
To create a Windows Application open up the new project dialog
by selecting New then Solution from the File
menu. Select the Python category to show the available project
templates. Select the Windows Application project template, enter a
name and location and click the Create button.
Designing Windows Forms
The Windows Forms designer is not yet complete so be warned that
it could generate form code that will no longer compile.
The designer can be opened by opening a form in the text editor
and selecting the Design tab at the bottom of the editor.
Once open in the designer you can add controls to the form by
dragging the controls from the Tools window. In the screenshot
below a label, text box and a button have been added.
Click the Source tab at the bottom of the editor to view the
generated code in the InitializeComponents method.
Limitations
The IronPython forms designer is not yet complete and the
following are some of the known limitations.
No support for project or local form resources.
No support for icons.
Incomplete support for ToolStripItems and menu strips.
Incomplete support for ListViewItems.
No support for TreeViewItems.
Incomplete support for non-visual components (e.g.
Timers).
Controls needed to be fully namespace qualified.
Forms Designer Internals
For those interested in how the forms designer actually works at
a high level we will now look at what the IronPython forms designer
does when loading and then generating code for a form.
To show the form in the designer the following steps are
executed.
The form's code is parsed and an IronPython AST
(PythonAst object) is created.
The AST is then visited and each control is added to the
forms designer and the control's properties are set.
The form's properties are set in the designer and the
form is displayed.
To generate the code after the form has been designed the
following steps are executed.
The form is obtained from the forms designer.
Each of the child components of the form have their
properties checked to see if they need to be serialized. This can
be done by getting all the
property descriptors and then checking the
ShouldSerializeValue method. If they do need to be serialized
then code is generated for them and added to a StringBuilder.
After all the child components are added the code for the
form is generated.
Finally the generated code is inserted into the text editor
inside the InitializeComponent method, replacing any existing
code.
[Less]
|
|
Posted
almost 17 years
ago
by
MattWard
SharpDevelop 3.1 now supports converting C# and VB.NET code to
IronPython. It can convert a single file or an entire project. The
code to convert between these languages is still under development
and has some limitations.
... [More]
Converting an Individual File
To convert a C# or VB.NET file, open it in SharpDevelop's
text editor, then from Tools menu select Convert code to
Python.
The code conversion is limited to converting classes so it will
not convert an arbitary piece of code that is not inside a
class.
Converting a Project
To convert a C# or VB.NET project, open it in SharpDevelop, then
from the Project menu select Convert From C# to
Python.
Once converted the project will most likely not compile straight
away due to limitations in the implementation. At the time of
writing converting a project has the following limitations:
Project's Main File is not set.
No code generated to call the project's Main entry
method.
Namespace imports do include all the used classes.
Code Conversion Internals
Converting code to IronPython was originally supported in
SharpDevelop 2.2 and was based on converting code to a
Microsoft CodeDOM and then getting IronPython 1.0 to generate
the Python code. In IronPython 2.0 this CodeDOM support was removed
so the code conversion feature was removed from SharpDevelop 3.0
since that was using IronPython 2.0. In SharpDevelop 3.1 the code
conversion has been rewritten to no longer use the CodeDOM support.
It now works by executing the following simple steps:
The C# or VB.NET code is parsed using SharpDevelop's
parsing library
NRefactory and an
abstract syntax tree (AST) is generated.
A
visitor class then walks this AST and generates Python code
which is added to a StringBuilder.
Once the visit is complete the generated Python code is then
displayed or saved to disk.
[Less]
|
|
Posted
almost 17 years
ago
by
MattWard
SharpDevelop 3.1 now supports NUnit
2.5.
A summary of which NUnit version is supported by SharpDevelop is
shown in the table below.
SharpDevelop 3.1
NUnit 2.5
SharpDevelop 3.0
NUnit 2.4.8
SharpDevelop 2.2.1
NUnit
... [More]
2.4.7
SharpDevelop 1.1
NUnit 2.2
NUnit 2.5 Changes
NUnit 2.5 has changed quite substantially compared with the
previous 2.4.8 release, as outlined in the
NUnit
2.5 release notes. The problems that we had when migrating
SharpDevelop's unit tests to NUnit 2.5 were as follows.
Assert.IsInstanceOfType has been replaced by
Assert.IsInstanceOf.
Your code will still compile and work if
Assert.IsInstanceOfType is used but you will get compiler
warnings.
NUnit.Framework.SyntaxHelpers namespace no longer exists.
All classes that were in this namespace have been moved to
the NUnit.Framework namespace.
The Has.Count constraint no longer takes an integer
parameter.
To fix this problem replace code such as:
Assert.That(classesCollection, Has.Count(1));
With the following:
Assert.That(classesCollection.Has.Count.EqualTo(1));
Overriding a [TestFixtureSetUp] method in a derived class
using the new keyword no longer works.
Some of the SharpDevelop unit tests were overriding an
abstract base test class [TestFixtureSetUp] method in a derived
class by using the new keyword, as shown below.
Base class:
[TestFixtureSetUp] public void SetUpFixture() { // Setup code. }
Derived class:
[TestFixtureSetUp] public new void SetUpFixture() { // Extra setup code. base.SetUpFixture(); }
In NUnit 2.4.8 the SetUpFixture method in the derived class
would be called when running the tests allowing it to execute
some extra setup steps. In NUnit 2.5 the base class
SetUpFixture method is called instead and the derived class
method is never called. To resolve the problem we changed the
base class so it used a virtual method and allowed the derived
class to override this to execute its extra setup steps.
Base class:
[TestFixtureSetUp] public void SetUpFixture() { BeforeSetUpFixture(); // Setup code. } public virtual void BeforeSetUpFixture() { }
Derived class:
public override void BeforeSetUpFixture() { // Extra setup code. }
[Less]
|