We have a software system that should support multiple variants. Each variant should contain a customized version of one or more UI components (Windows Forms in this case).
A prototype has been created in VS2017 and has the following solution;
ProductFoo (Solution)
MainApplication (Windows Application)
MainApplicationForm1.cs (Windows Forms)
XY1 (Class library)
Form1.cs ((Windows Forms)
XY2 (Class library)
Form1.cs ((Windows Forms)
In this simple prototype, the MainApplicationForm1 forms contain one button that when clicked should either show Form1.cs in XY1 og XY2 library depending on which variant is selected.
To solve this we have updated Solution Manager with following solution configurations;
XY1_Debug
XY1_Release
XY2_Debug
XY2_Release
Then we added conditional compilation symbols for MainApplication.
The solution configurations XY1_Debug and XY1_Release use the conditional symbol XY1
The solution configurations XY2_Debug and XY2_Release use the conditional symbol XY2
Then we added reference from MainApplication to both XY1 and XY2 projects.
Lastly, we added the following code in MainApplicationForm1.cs
public partial class MainAppForm1 : Form
{
public MainAppForm1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
#if XY1
XY1.Form1 form = new XY1.Form1();
form.ShowDialog();
#elif XY2
XY2.Form1 f1 = new XY2.Form1();
f1.ShowDialog();
#else
#error The MainApplication is missing Form1
#endif
}
}
This solution works but I have reservations with using preprocessor directives. The code looks messy and can quickly become difficult to maintain. What are the best practices for this kind of scenario?
Appreciate any input.
Your question is quite broad and referes to the base structure of the project you want to have.
The way you choose is close to the Feature toggling, just done based on the build configuration. Ususaly it sould be something like:
if(features.IsOn("XY1-feature")){
XY1.Form1 form = new XY1.Form1();
form.ShowDialog();
}
Classical way can give you more flexibility. E.g. moving feature toggless to the config would give you a possibility to dynamically toggle different features for specific deployment, but, as impact, it would encrease the complexity and would require more testing
I would suggest you to take a deeper look into Dependency injection and Strategy pattern
As an alternative to the Feature toggling you can use branching. Create a specific branch for the specific project/client. That could bring you problems with merging, but would keep your cleaner for a specific implementation. It would fit best to the project with lots of minor differences from project to project
I suggest using two radio buttons to solve this problem. This is a very easy way.
Select radioButton1, pop up XY1.Form
Select radioButton2, pop up XY2.Form
MainApplicationForm1.cs:
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
XY1.Form1 form = new XY1.Form1();
form.Show();
}
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
if (radioButton2.Checked)
{
XY2.Form1 f1 = new XY2.Form1();
f1.Show();
}
}
Related
I am creating this question and answering it myself to share an easy way to run/debug and build an application (Windows Forms), using ActiViz in the .NET Framework (C#). It’s OK to Ask and Answer Your Own Questions.
If I create a new Windows Forms application (.NET Framework, C#) and target x64 platforms:
I can then go to Project > Manage NuGet Packages and search/install Activiz.NET.x64 (v5.8.0).
However, after installing Activiz.NET.x64, if I try to drag the RenderWindowControl to a Form, the following error is shown (Failed to load toolbox item 'RenderWindowControl'.):
Is there a workaround for this issue?
I know this question has been answered here and here (answer not accepted); however, these answers consist in designing/debugging the application with Activiz.NET.x86 (32 bit) and only installing Activiz.NET.x64 (64 bit) for the release version of the application. It is obviously very cumbersome to switch back and forth between both ActiViz packages.
In the FAQ of the Kitware ActiViz website the following is presented:
Does ActiViz 64 work with Visual Studio?
Visual Studio is a 32 bits
application, therefore 64 bits control does not work and you need the
32 bits version of ActiViz when using the designer within Visual
Studio. Usually, the 32 bits version is used to design and the 64 bits
version is used for the final compilation.
However, a workaround for this issue is to:
Create your new Windows Forms application (.NET C#) and immediately target x64 (64 bit) platforms;
Install Activiz.NET.x64 (v5.8.0 at the time of writing this answer) through Project > Manage NuGet Packages;
Add a Panel (named viewportPanel, for example) to your Form. This Panel will be the Parent to a RenderWindowControl;
Create a RenderWindowControl instance in the form's constructor like so:
using Kitware.VTK;
using System.Windows.Forms;
namespace ActiVizTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
RenderWindowControl renderWindowControl = new RenderWindowControl()
{
Parent = viewportPanel,
AddTestActors = true
};
}
}
}
You can now run/debug and build the application while using Activiz.NET.x64:
However, there is still a possible issue:
Lets say that I want the background to be red.
public Form1()
{
InitializeComponent();
RenderWindowControl renderWindowControl = new RenderWindowControl()
{
Parent = viewportPanel,
AddTestActors = true
};
renderWindowControl.RenderWindow.GetRenderers().GetFirstRenderer().SetBackground(1, 0, 0);
}
Adding the new line of code shown above will raise a System.NullReferenceException. This is because renWinControl.RenderWindow is null before Form1 is initialized.
So, any setup we need to do on the RenderWindow should be done after the form's constructor, for example, we can create a Load event. And while we are at it, we can also create some fields to have easier access to the RenderWindow and Renderer.
Full code:
using Kitware.VTK;
using System;
using System.Windows.Forms;
namespace ActiVizTest
{
public partial class Form1 : Form
{
private readonly RenderWindowControl renderWindowControl;
private vtkRenderWindow renderWindow;
private vtkRenderer renderer;
public Form1()
{
InitializeComponent();
renderWindowControl = new RenderWindowControl()
{
Parent = viewportPanel,
AddTestActors = true
};
}
private void Form1_Load(object sender, EventArgs e)
{
renderWindow = renderWindowControl.RenderWindow;
renderer = renderWindow.GetRenderers().GetFirstRenderer();
renderer.SetBackground(1, 0, 0);
}
}
}
And finally, debugging in x64:
EDIT
Instead of using the Form_Load event, it is best to use the RenderWindowControl_Load event. Just remember that RenderWindowControl.RenderWindow is null before the control is loaded.
I have successfully built a C# Word 2013 project (ReportGenerator) that opens an MS ACCESS database and generates a MS WORD 2013 report. The results are very good. The issue I have is at the moment it can only be run from inside Visual Studio. My boss wants it to run via a windows form.
I have the competence to build a new project (ReportRunner) that contains a windows form with a datagrid, populate it and put a button on it. What I lack is the competence to know how to:
Open the report generation code from ReportGenerator in the
onclick event of ReportRunner
Pass a variable from ReportRunner to ReportGenerator so to avoid
hard coding.
I was expecting to be able to write a line like “ReportGenerator.ThisDocument.ThisDocument_Startup” in the click event of the button. This isn't happening.
The significant bits of code in my projects are:
ReportGenerator
namespace ReportGenerator
{
public partial class ThisDocument
{
ReportData reportData = new ReportData();
public void ThisDocument_Startup(object sender, System.EventArgs e)
{
int idToLookFor = 2;
reportData = MyFunctionToReadAccessData(idToLookFor);
MyFunctionToPutDataIntoReport();
}
}
}
ReportRunner
using ReportGenerator;
namespace ReportRunner
{
public partial class Form1 : Form
private void button1_Click(object sender, EventArgs e)
{
int idToLookFor = int.Parse(dataGridView1.CurrentRow.Cells[0].Value.ToString());
//HOW DO I MAKE IT OPEN REPORT GENERATOR ThisDocument_Startup
// AND PASS IT THE idToLookFor
}
}
Update:
I'm having trouble understanding your comment so here's a few updates:
You can call method from a Document-level Addin from a seperate C# WinForm using the link I provided. It doesn't matter if it's an Application-level addin or a Document-level addin - the approach is the same. See this link.
Why did you build a ReportRunner Form project that is separate from your ReportGenerator Add-in project? As I said below, you can create a single VS solution with 2 projects - one is a Document-level addin, the other is a WinForm and you can simply call the WinForm from the Ribbon associated with the addin.
I assume that you're asking how to call a function from a Word Addin from a Winform? I recently explained how to do this here: How to call a VSTO AddIn method from a separate C# project?
That being said, I don't recommend doing this becaues you can simply package your WinForm together with your Addin and then open it like this using a Ribbon:
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Form1 aForm = new Form1();
aForm.Show();
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);
I have a question related to the error on the title. Im working with c# and Visual Studio 2010.
I have a form declared as "public class FormularioGeneral : Form", which is the base for the rest of the forms in my application. When i try to access the Designer View i get this error several times, as you can see in the image:
All the errors references lines inside the InitializeComponent method, where the value is assigned to a property like this one:
[...]
this.PanelMargenIzquierdoCapaBase.BackColor = m_ColorCapaBase;
[...]
But all the variables are declared in the same class as read-only properties and all of them are assigned inside a method which is called in the constructor.
Declaration of properties:
protected Color m_VariableName;
public Color VariableName
{
get { return m_VariableName; }
set { }
}
Constructor code:
public FormularioGeneral()
{
ConfigurarUI();
AccionesConstructor();
InitializeComponent();
PostInicializacionComponentes();
EstablecerIcono();
InicializarLocalizacionFormulario();
}
ConfigurarUI method:
public virtual void ConfigurarUI()
{
[...]
m_AltoBordeSuperiorCapaBase = 30;
m_AltoBordeInferiorCapaBase = 7;
m_AnchoBordesLateralesCapaBase = 7;
m_ColorCapaBase = Color.FromArgb(50, 100, 150);
m_ColorTextoCapaBase = Color.White;
m_ColorTextoBotonAplicacion = Color.Black;
m_FuenteTextoIzquierdoCapaBase = new System.Drawing.Font("Verdana", 11.0F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
m_FuenteTextoCentroCapaBase = new System.Drawing.Font("Verdana", 14.0F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
[...]
}
So, as far as i know, all the variable which are giving the errors are correctly declared and have a value assigned before the InitilizeComponent function is called.
Im stuck at this point and dont know what to do to solve the problem. Hope some of you can help me with this issue.
So, I have had the same problem in the past, for fix I did the following:
Solution → Clean Solution;
Build → Rebuild Solution;
Close Visual Studio, re-open.
Thanks a lot to Marshall Belew!
In my case, I had an older Windows Forms project where InitializeComponents() started like this:
private void InitializeComponent()
{
var componentResourceManager = new ComponentResourceManager(typeof(MyForm));
...
This resulted in an error message later on when accessing the componentResourceManager inside InitializeComponent():
The variable 'componentResourceManager' is either undeclared or was never assigned.
When comparing with a newly created form, I saw that it was similar to my non-working form, except for one thing:
The variable was not named componentResourceManager but simply resources.
Solution
After doing a rename on my variable to also have the name resources, everything works successfully:
private void InitializeComponent()
{
var resources = new ComponentResourceManager(typeof(MyForm));
...
The Windows Forms Designer in Visual Studio 2017 did open the form correctly.
I ran into this error because my project is x64 only. Apparently Visual Studio, being a 32bit application, cannot load any forms or controls compiled to 64bit in the designer. It makes total sense, but the error gives you no indication that is the problem.
See the answer to Visual studio designer in x64 doesn't work.
The workaround is to change your project to Any CPU when designing, then back when building.
Maybe the error occurs due to your constructor code. Place InitializeComponent(); at the beginning of the constructor like this:
public FormularioGeneral()
{
InitializeComponent();
ConfigurarUI();
AccionesConstructor();
PostInicializacionComponentes();
EstablecerIcono();
InicializarLocalizacionFormulario();
}
Explanation:
The variables are initialized in that method.
I had the same problem and cleaning and rebuilding did not work for me.
In my case the problem was caused by the Visual Studio designer loading referenced DLLs from the GAC instead of loading them from the <HintPath> directory specified in the .csproj file. The DLLs in the GAC did not have the same version as the locally stored DLLs.
When I updated the DLLs in the GAC to have the same version everything worked OK again.
I had this issue when my user control had some code in the constructor which was related to runtime resource. I added null check and it fixed.
InitializeComponent();
if (MyConfig!= null)
{
this.label2.Text = MyConfig.text1;
this.label3.Text = MyConfig.text2;
this.label1.Text = MyConfig.text3;
}
In my case, I added a third party control in my Toolbar(via a .dll file), and draw one of it in my form. And for some reason, my Toolbar clean this third party control out of the general group(I added it in the general group), so VS cannot find this control.
Here is what I done to slove this problem:
Add this control into the Toolbar.
Clean the solution
Rebuild the solution
Redraw the control if neccessary.
Don't put anything other than InitializeComponent(); in the constructor. You can put the code from there in events like Load().
User Controls were caused problem and after trying all suggestions, (Focus solution then Alt+Enter) changing solution's Platform Target from x64 to Any CPU solved the problem.
About the variables, can you simply initialize them in the declaration? I think that would suffice, even if you change the value later. From what I'm seeing, the compiler is unable to check whether you have initialized them or not because it's not directly on the constructor code, it's being done on a virtual method which will evaluate only at runtime.
So, instead of:
protected Color m_VariableName;
public Color VariableName
{
get { return m_VariableName; }
set { }
}
Do:
protected Color m_VariableName = Color.White; // Or null
public Color VariableName
{
get { return m_VariableName; }
set { }
}
And a comment: you should avoid virtual calls in the constructor, which can lead to obscure errors in your application. Check it here.
This error occurs for me while creating a third party control in InitializeComponent() which is called from form constructor. When I created it after InitializeComponent() it works fine for me.
public MyForm() //Form constructor
{
InitializeComponent();
//Create/initialize third party control here with new operator
}
I am working with WPF inside of Windows Forms.
I hosted my WPF User Control in a Windows Forms Element Host. Due to that when InitializeComponent() got called I executed Code before reaching the InitializeComponent() of my WPF control. Tricky.
So I moved it out of my constructor, Clean Build, Rebuild, Restart VS and everything works as expected. Finally.
I have had the same problem and I fixed it. Actually Visual Studio only works with X86 controls and you can't create a user control in X64 mode and use it.
You should add a new class library in Any CPU mode and build the class library. then you can add its DLL in your project. Done.
If it doesn't you must go to the Configuration manager and set the Active solution platform to X64 also do that for all subprojects. Remember that build option has to be checked. and go to the properties of the class library and click on the build tab. then set the platform target to Any CPU.
First I had code that referenced something a type that the designer couldn't load (for whatever reason). Then I had code in the constructor that couldn't be executed from my local laptop. I decided the best option was to move the logic to the Load event and check if the component was in DesignMode and exit if it was.
Even this wasn't enough for me as the designer still tried to JIT the type that was later down in the method, so I had to move it out to a separate method to stop that from happening. Here's basically what I ended up with:
private void userControl_Load(object sender, EventArgs e)
{
if (DesignMode) return;
Initialize();
}
private void Initialize()
{
// do your work
}
Special thanks to this SO answer which pointed me to a comment in a blog post about not accessing the DesignMode property until you're in the Load event...
Renaming the variable componentResourceManager to resources solved error.
Unfortunately i had to change a ton of other items to get the designer working for Telerik reports designer
In my Solution i had wrong Reference paths which i fixed in the .csproj files. After fixing that i could finally load the Form again.
I have two projects : Menu and Module and they are both in the same namespace foobar.
I am currently referencing the module project from the Menu project to open up certain controls on a tab control in my menu. However I need to launch a new control from one of my controls which is located in the Module project.
When I try referencing the menu project, it does not show up in my intellisense when I try to reference it with a using. Am I doing something wrong logically here?
Here is an example of what it is :
Project Menu
Public Void LaunchWPFControl(string pHeader,string pPath)
{
//Code goes here to launch a WPF control in the browser
}
Project Module
//What I would love to do but doesn't work
Using Menu;
...
...
...
private void dgModule_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
Menu.LaunchWPFControl("Accounts","AccountsControl");
}
If you are talking about seperate projects then what you are trying to do here is a circular reference, this is not allowed. If Project Menu references Project Module, then Project Module cannot reference Project Menu.
If you need a class in Project Module to trigger something in the Menu project you need to look for an alternative way of doing it. One possible technique for achieving this is to create an event in the class in the Module project that the Menu project can subscribe to and perform the required action.
For example in Project Module:
private void dgModule_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
OnMyEvent();
}
private void OnMyEvent()
{
EventHandler localEvent = MyEvent;
if(localEvent != null)
{
localEvent(this, EventArgs.Empty);
}
}
Then in Project Menu you can subscribe to this event and perform your action:
...
...
...
moduleClass.MyEvent += SomeHandler;
...
...
...
private void SomeHandler(Object sender, EventArgs e)
{
Menu.LaunchWPFControl("Accounts","AccountsControl");
}
As Ray Burns mentions (see comments) another way would be to define an interface of the Menu class in some shared location (either there referenced project, or some other shared project) and than you can pass implementations of that interface to the Module project.
Which way is better often depends on the abstraction you are trying to achieve with each project.
If they're both in namespace foobar then you need
using foobar;
instead of using Menu.
It's important that you understand the terminology involved - you're talking about "a different project in the same assembly" - that's nonsensical. Visual Studio creates one assembly per project. You then talk about the projects having the same namespace in the text of your question. You need to understand the difference.
To work out the namespace of a type, open the class containing the type and look for namespace declarations:
namespace MyProject
{
...
}
To work out the assembly of a type, look in the project properties for the project in which it's declared - or if you're using the same solution, just add a reference from the project which wants to use the type to the project which declares the type.
Note that you specify a namespace with a using directive; you need to add a reference to an assembly in solution explorer. They're not doing the same thing. A using directive just says, "Within the code affected by this using directive, I want to be able to use types within this namespace without fully qualifying the names."
Next you've got code like this:
Menu.LaunchWPFControl("Accounts","AccountsControl");
I thought Menu was either a project name or a namespace - but now you're trying to use it as a type name. Which is it?
If this doesn't sort you out, please post full code and a more coherent description of the projects and namespaces involved. Take a step back, work out the types, namespaces and assemblies involved, and then lay it all out clearly.
So Project Menu references Project Module. Then you want Project Module to reference Project Menu so a module can directly call Menu functionality?
This isn't possible, this is a circular reference. If you try to add the Menu reference to the Module project, Visual Studio won't let you.
You need to pull the stuff out of Menu that both Menu and Module want to use into a third project, and have them both reference it. Or combine Menu and Module into one project.