I have these objects in my project:
SchedulerList
SchedulerListItem
SchedulerListItemDetails
each one is a win forms control, which are used in forms of my application. The SchedulerList holds SchedulerListItems and each item can have SchedulerListItemDetails.
my code goes as follows:
//creating my initial list form
FrmListTesting f = new FrmListTesting();
f.Show();
The form has only one button that has a hard-coded parameter for testing purposes, as well as a SchedulerList control taht will hold the list items.
When the button is clicked the form does the following:
private void button1_Click(object sender, EventArgs e)
{
var control = this.Controls[1] as SchedulerList;
var path = #"D:\Share\Countries.txt";
var sli = new SchedulerListItem(path);
control.AddItem(sli);
}
my SchedulerListItem constuctor goes as follows:
public SchedulerListItem(string path)
{
InitializeComponent();
this.Name = Path.GetFileNameWithoutExtension(path);
this.SourcePath = path;
this.DestinationPath = GetDestinationPath(path);
}
And the AddItem method is defined as:
public void AddItem(SchedulerListItem item)
{
this.flPanel.Controls.Add(item);
}
The add item method works as intended, displays all the data that was required and displays it in the UI. The list item has a button that brings up the details form as such:
//the form constructor
public FrmSchedulerItemDetails(SchedulerListItem item)
{
InitializeComponent();
this.detailsControl = new SchedulerListItemDetails(item, this);
}
//control constructor
public SchedulerListItemDetails(SchedulerListItem item, Form owner)
{
InitializeComponent();
this.SourcePath = item.SourcePath;
this.DestinationPath = item.DestinationPath;
this.OldFormat = item.OldFormat;
this.ExportToExcel = item.ExportToExcel;
this.owner = owner;
this.underlyingItem = item;
}
And now the problem. After the SchedulerListItemDetails constructor is called and the data "gets initialized", when i look at the data inside the object its set to default values. it seams that everything that I set after InitializeComponent(); gets ignored.
things that i have tried:
hard-coding the values to see if primitives get passed correctly
settings breakpoints on every InitializeComponent() method to see the stack trace associated with setting to default values
none of the methods show any results... I know that if i use a form directly instead of using a control within a from i can set the values the way i want to, but I'm very confused as to why this other method with controls doesn't work.
EDIT 1:
the code for SchedulerListItemDetails:
public partial class SchedulerListItemDetails : UserControl
{
public SchedulerListItemDetails(SchedulerListItem item, Form owner)
{
InitializeComponent();
this.SourcePath = item.SourcePath;
this.DestinationPath = item.DestinationPath;
this.OldFormat = item.OldFormat;
this.ExportToExcel = item.ExportToExcel;
this.owner = owner;
this.underlyingItem = item;
}
public SchedulerListItemDetails()
{
InitializeComponent();
}
private Form owner = null;
private SchedulerListItem underlyingItem;
public Boolean ExportToExcel
{
get
{
return this.cbxExcel.Checked;
}
set
{
this.cbxExcel.Checked = value;
}
}
public Boolean OldFormat
{
get
{
return this.cbxOldFormat.Checked;
}
set
{
this.cbxOldFormat.Checked = value;
}
}
public String DestinationPath
{
get
{
return this.tbxDestinationPath.Text;
}
set
{
this.tbxDestinationPath.Text = value;
}
}
public String SourcePath
{
get
{
return this.tbxSourcePath.Text;
}
set
{
this.tbxSourcePath.Text = value;
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.owner.Close();
}
private void btnSave_Click(object sender, EventArgs e)
{
underlyingItem.SourcePath = this.SourcePath;
underlyingItem.DestinationPath = this.DestinationPath;
underlyingItem.OldFormat = this.OldFormat;
underlyingItem.ExportToExcel = this.ExportToExcel;
btnCancel_Click(sender, e);
}
}
I'll make an answer, because it should help you to solve your problem.
You have default (parameterless) constructor, which may be called and if it is called, then your constructor with parameters is not called.
Proper design would be something like
public partial class SchedulerListItemDetails : UserControl
{
public SchedulerListItemDetails()
{
InitializeComponent();
}
public SchedulerListItemDetails(SchedulerListItem item, Form owner): this()
{
this.SourcePath = item.SourcePath;
...
}
}
Notice this(), this ensure what parameterless constructor is called before (and InitializeComponent() as well, no need to duplicate it in another constructor).
Back to your problem. In your case it's like this
public partial class SchedulerListItemDetails : UserControl
{
public SchedulerListItemDetails()
{
InitializeComponent();
}
public SchedulerListItemDetails(SchedulerListItem item, Form owner)
{
InitializeComponent();
this.SourcePath = item.SourcePath;
...
}
}
Only one constructor can be called. So if you put breakpoint in parameterless one and it's triggered, then you have problems. Because you create somewhere SchedulerListItemDetails without setting it's properties (they stay default).
More likely problem is that you create new instance of that object (either before or after constructing proper, if your code ever construct such object) and that instance is what you inspect later.
So after i got a quick course of how win forms work i figured out what the problem was.
my code that i thought was enough is:
public FrmSchedulerItemDetails(SchedulerListItem item)
{
InitializeComponent();
this.DetailsControl = new SchedulerListItemDetails(item, this);
}
public SchedulerListItemDetails DetailsControl
{
get
{
return this.detailsControl;
}
set
{
this.detailsControl = value;
}
}
the this.detailsControl is the control im trying to setup, but as i have learned the correct way of replacing a component for a new one is:
public FrmSchedulerItemDetails(SchedulerListItem item)
{
InitializeComponent();
this.DetailsControl = new SchedulerListItemDetails(item, this);
}
public SchedulerListItemDetails DetailsControl
{
get
{
return this.detailsControl;
}
set
{
this.Controls.Remove(this.detailsControl);
this.detailsControl = value;
this.Controls.Add(this.detailsControl);
}
}
Feel kinda silly now :).
Related
I'm trying to get listbox selected item from one form 1 to display on textbox on form 2.
So far it's working partly.
The problem is that it only gets the selectedItem that was selected at the start of the application. If the user selects a new item, it still gets the one that was selected as default.
Form 1 MainForm:
public MainForm()
{
public string GetListBoxSelectedItem()
{
if (Animallst.SelectedItem != null) //Animallst is the listbox
{
return Animallst.SelectedItem.ToString();
return string.Empty;
}
}
private void foodbtn_Click(object sender, EventArgs e)
{
FoodRegister foodForm = new FoodRegister();
foodForm.Show();
}
}
Form 2 FoodRegister:
public partial class FoodRegister : Form
{
private RecipeManager m_foodmanager = new RecipeManager();
public FoodRegister()
{
InitializeComponent();
MainForm main = new MainForm();
Nametxt.Text = main.GetListBoxSelectedItem();
//My initializations
InitializeGUI();
}
}
These two lines are not at all doing what you want them to do. You're creating an entirely new instance of MainForm, which has nothing to do with the original instance. And so GetListBoxSelectedItem() doesn't do what you want either.
MainForm main = new MainForm();
Nametxt.Text = main.GetListBoxSelectedItem();
Instead, pass a reference to the original Form into the second Form:
public FoodRegister(MainForm main)
{
InitializeComponent();
Nametxt.Text = main.GetListBoxSelectedItem();
...
And then call it like this:
FoodRegister foodForm = new FoodRegister(this);
foodForm.Show();
A couple of things to mention:
The line return string.Empty is redundant. Because of the line above it, this line becomes unreachable
In your FoodRegister for, you create a new instance of your main form. This then wipes anything that the main form was holding - i.e. Animallst.SelectedItem.ToString();
An easy way to handle this is to set the value to a static variable - that way you won't have to create a new instance of the form to access it.
Main form:
public static string GetListBoxSelectedItem()
{
if (Animallst.SelectedItem != null) //Animallst is the listbox
{
return Animallst.SelectedItem.ToString();
}
else { return string.Empty(); }
}
Food Register:
public FoodRegister()
{
InitializeComponent();
MainForm.GetListBoxSelectedItem();
//My initializations
InitializeGUI();
}
Haven't played with WinForms in awhile but here goes
In Form 2
public partial class FoodRegister : Form
{
private RecipeManager m_foodmanager = new RecipeManager();
public FoodRegister()
{
InitializeComponent();
//My initializations
InitializeGUI();
}
public void SetText(string txt)
{
Nametxt.Text = txt;
}
}
In Form 1
public MainForm()
{
private readonly FoodRegister foodForm = new FoodRegister();
private void foodbtn_Click(object sender, EventArgs e)
{
foodForm.SetText(Animallst.SelectedItem == null ? "" : Animallst.SelectedItem.ToString());
foodForm.Show();
}
}
I replaced
GetListBoxSelectedItem()
with
Animallst.SelectedItem == null ? "" : Animallst.SelectedItem.ToString()
On my form, I have one Panel container, named "panelShowList".
On my project, i added a new class, which look like this:
class myNewClass
{
private int newPanelPos = 30;
private const int spaceBetweenElements = 30;
private const int panelWidth = 90;
private const int panelHeight = 40;
private int elementPos = 0;
private ArrayList myPanels = new ArrayList() { };
// some irelevant methods
public void addElementPanels(Panel dataPanel, Panel nextPanel)
{
myPanels.Add(dataPanel);
myPanels.Add(nextPanel);
}
public void displayPanels()
{
foreach (Panel tmp in myPanels)
{
// here i'm stuck
// i need to do something like this :
// myMainForm.panelShowList.Controls.Add(tmp);
// of course this is wrong! but i need a method to acces that control
}
}
}
Basically, I need a way to add all Panels from my ArrayList on "panelShowList" control from my form.
I tried something like this:
public void displayPanels()
{
frmMain f = new frmMain();
foreach (Panel tmp in myPanels)
{
f.display(tmp);
// where display(Panel tmp) is a function in my Form, who access
// "panelShowList" control and add a new Panel
}
}
But it only works if i do this:
f.ShowDialog();
and another form is open.
Any suggestions will be appreciated.
Maybe a bit late, but by all means, here is another approach, that's still more clean than David's approach:
You should add an EventHandler in your MyNewClass. Then you can subscribe to that event from within your form.
public partial class Form1 : Form
{
private readonly MyNewClass _myNewClass;
public Form1()
{
InitializeComponent();
_myNewClass = new MyNewClass();
_myNewClass.DisplayPanelsInvoked += DisplayPanelsInvoked;
}
private void DisplayPanelsInvoked(object sender, DisplayPanelsEventArgs e)
{
var panels = e.Panels; // Add the panels somewhere on the UI ;)
}
}
internal class MyNewClass
{
private IList<Panel> _panels = new List<Panel>();
public void AddPanel(Panel panel)
{
_panels.Add(panel);
}
public void DisplayPanels()
{
OnDisplayPanels(new DisplayPanelsEventArgs(_panels));
}
protected virtual void OnDisplayPanels(DisplayPanelsEventArgs e)
{
EventHandler<DisplayPanelsEventArgs> handler = DisplayPanelsInvoked;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<DisplayPanelsEventArgs> DisplayPanelsInvoked;
}
internal class DisplayPanelsEventArgs : EventArgs
{
public DisplayPanelsEventArgs(IList<Panel> panels)
{
Panels = panels;
}
public IList<Panel> Panels { get; private set; }
}
In my opinion it's a better solution, because you don't need to provide a reference of the form to the MyNewClass instance. So this approach reduces coupling, because only the form has a dependency to the MyNewClass.
If you always want to "update" the form whenever a panel is added, you could remove the DisplayPanels-method and shorten the code to this:
public partial class Form1 : Form
{
private readonly MyNewClass _myNewClass;
public Form1()
{
InitializeComponent();
_myNewClass = new MyNewClass();
_myNewClass.PanelAdded += PanelAdded;
}
private void PanelAdded(object sender, DisplayPanelsEventArgs e)
{
var panels = e.AllPanels; // Add the panels somewhere on the UI ;)
}
}
internal class MyNewClass
{
private IList<Panel> _panels = new List<Panel>();
public void AddPanel(Panel panel)
{
_panels.Add(panel);
OnPanelAdded(new DisplayPanelsEventArgs(_panels, panel)); // raise event, everytime a panel is added
}
protected virtual void OnPanelAdded(DisplayPanelsEventArgs e)
{
EventHandler<DisplayPanelsEventArgs> handler = PanelAdded;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<DisplayPanelsEventArgs> PanelAdded;
}
internal class DisplayPanelsEventArgs : EventArgs
{
public DisplayPanelsEventArgs(IList<Panel> allPanels, Panel panelAddedLast)
{
AllPanels = allPanels;
PanelAddedLast = panelAddedLast;
}
public IList<Panel> AllPanels { get; private set; }
public Panel PanelAddedLast { get; private set; }
}
and another form is open
That's because you're creating an entirely new form:
frmMain f = new frmMain();
If you want to modify the state of an existing form, that code will need a reference to that form. There are a number of ways to do this. One could be to simply pass a reference to that method:
public void displayPanels(frmMain myMainForm)
{
foreach (Panel tmp in myPanels)
{
// myMainForm.panelShowList.Controls.Add(tmp);
// etc.
}
}
Then when your main form invokes that method, it supplies a reference to itself:
instanceOfNewClass.displayPanels(this);
Though, to be honest, it's not really clear what sort of structure you're going for here. If code is modifying a form then I imagine that code should be on that form. It can certainly be organized into a class, but perhaps that can be an inner class of that form since nothing else needs to know about it.
I'm also concerned that your implementation of myNewClass requires methods to be invoked in a specific order. Any given operation on an object should fully encapsulate the logic to complete that operation. Some of that initialization logic may belong in the constructor if the object isn't in a valid state until that logic is completed.
This is all a bit conjecture though, since the object structure isn't clear here.
I need to load a User Control in my panel1 inside Form1.cs, the problem is that the UserControl (AudioPlaybackPanel) contains an ImportingConstructor ([ImportMany]IEnumerable<>) and I can't figure out what two arguments I should have in the Form1 AudioPlaybackPanel(????).
The error I get is: 'NAudio.App.AudioPlaybackPanel' does not contain a constructor that takes 0 arguments
Here is the Form1.cs
namespace NAudio.App
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
AudioPlaybackPanel myPanel = new AudioPlaybackPanel(????);
panel1.Controls.Add(myPanel);
}
}
}
And this is my User Control Panel (AudioPlaybackPanel.cs):
namespace NAudio.App
{
[Export]
public partial class AudioPlaybackPanel : UserControl
{
private IWavePlayer waveOut;
private string fileName = null;
private WaveStream fileWaveStream;
private Action<float> setVolumeDelegate;
[ImportingConstructor]
public AudioPlaybackPanel([ImportMany]IEnumerable<IOutputDevicePlugin> outputDevicePlugins)
{
InitializeComponent();
LoadOutputDevicePlugins(outputDevicePlugins);
}
[ImportMany(typeof(IInputFileFormatPlugin))]
public IEnumerable<IInputFileFormatPlugin> InputFileFormats { get; set; }
private void LoadOutputDevicePlugins(IEnumerable<IOutputDevicePlugin> outputDevicePlugins)
{
comboBoxOutputDevice.DisplayMember = "Name";
comboBoxOutputDevice.SelectedIndexChanged += new EventHandler(comboBoxOutputDevice_SelectedIndexChanged);
foreach (var outputDevicePlugin in outputDevicePlugins.OrderBy(p => p.Priority))
{
comboBoxOutputDevice.Items.Add(outputDevicePlugin);
}
comboBoxOutputDevice.SelectedIndex = 0;
}
void comboBoxOutputDevice_SelectedIndexChanged(object sender, EventArgs e)
{
panelOutputDeviceSettings.Controls.Clear();
Control settingsPanel;
if (SelectedOutputDevicePlugin.IsAvailable)
{
settingsPanel = SelectedOutputDevicePlugin.CreateSettingsPanel();
}
else
{
settingsPanel = new Label() { Text = "This output device is unavailable on your system", Dock=DockStyle.Fill };
}
panelOutputDeviceSettings.Controls.Add(settingsPanel);
}
private IOutputDevicePlugin SelectedOutputDevicePlugin
{
get { return (IOutputDevicePlugin)comboBoxOutputDevice.SelectedItem; }
}
// The rest of the code continues from here on...
}
}
Here is the Interface:
namespace NAudio.App
{
public interface IOutputDevicePlugin
{
IWavePlayer CreateDevice(int latency);
UserControl CreateSettingsPanel();
string Name { get; }
bool IsAvailable { get; }
int Priority { get; }
}
}
And just in case, here is one of the plugins:
DirectSoundOutPlugin.cs
namespace NAudio.App
{
[Export(typeof(IOutputDevicePlugin))]
class DirectSoundOutPlugin : IOutputDevicePlugin
{
private DirectSoundOutSettingsPanel settingsPanel;
private bool isAvailable;
public DirectSoundOutPlugin()
{
this.isAvailable = DirectSoundOut.Devices.Count() > 0;
}
public IWavePlayer CreateDevice(int latency)
{
return new DirectSoundOut(settingsPanel.SelectedDevice, latency);
}
public UserControl CreateSettingsPanel()
{
this.settingsPanel = new DirectSoundOutSettingsPanel();
return this.settingsPanel;
}
public string Name
{
get { return "DirectSound"; }
}
public bool IsAvailable
{
get { return isAvailable; }
}
public int Priority
{
get { return 3; }
}
}
}
Please help!
The error doesn't say it expects two arguments... it just says it doesn't take 0.
The constructor expects a single parameter - an IEnumerable<IOutputDevicePlugin>:
public AudioPlaybackPanel([ImportMany]IEnumerable<IOutputDevicePlugin> outputDevicePlugins)
{
...
}
You need to find something that implements the IOutputDevicePlugin interface and pass a collection of it, even if it's just an empty collection. (Passing null to the constructor will allow it to compile but will throw a runtime exception when you hit the loop in LoadOutputDevicePlugins.)
Considering the update to your question, something like this will get you up and running (although I doubt it means very much to pass an empty list):
var myPanel = new AudioPlaybackPanel(new List<DirectSoundOutPlugin>());
panel1.Controls.Add(myPanel);
It's worth asking whether you actually need to copy AudioPlaybackPanel.cs from the NAudio demo in its entirety. The reason it has this constructor is that it tries to demonstrate how you can use each and every one of NAudio's IWavePlayer implementations. But in a normal real-world application you would just select the one that was most appropriate for your use. e.g.
this.waveOut = new WaveOut();
waveOut.Init(new AudioFileReader("my file.mp3");
waveOut.Play();
So there's no need to incorporate the plug-in architecture from that particular demo, if all you want is just to play audio files.
I have a parent form and a dialog. I need to pass info from the parent to the dialog
Here's what I have:
private void Item_Click(object sender, EventArgs e)
{
DialogResult result = DialogResult.OK;
DlgGraphOptions _frmDlgGraphOptions = new DlgGraphOptions();
_frmDlgGraphOptions.m_SerOpts = theDGroup.m_SerOpts;
_frmDlgGraphOptions.ShowDialog(this);
if (result == DialogResult.OK)
{
// Save the revised options to the Data Group
theDGroup.m_SerOpts = _frmDlgGraphOptions.m_SerOpts;
}
In DlgGraphOptions(child/dialog) form, I have intialitzed
public partial class DlgGraphOptions : Form
{
public GraphOpts_t m_SerOpts = new GraphOpts_t();
}
private void InitSettings(int idxSeries)
{
m_nMaxPts = m_SerOpts.GetMaxPts(idxSeries);
}
So I need to pass theDGroup.m_SerOpts from parent to the dialog,so I have done
_frmDlgGraphOptions.m_SerOpts = theDGroup.m_SerOpts;
in the parent. Now in the child:
public GraphOpts_t m_SerOpts = new GraphOpts_t;
This seems to be wrong. I don't want to be reinitializing it.
I think you should change your code in this way:
First, in the DlgGraphOptions form, change the constructor of DlgGraphOptions
// Force the caller to pass a GraphOpts_t
// Check if it is a valid instance or create one as new
public partial class DlgGraphOptions(GraphOpts_t input ) : Form
{
m_SerOpts = (input == null ? new GraphOpts_t() : input);
}
then create a public property with only the getter returning the internal GraphOpts
public GraphOpts_t Options
{
get{ return m_SerOpts; }
}
then, in the calling form, change uour code
// Pass the m_setOpts from theDGroup
DlgGraphOptions _frmDlgGraphOptions = new DlgGraphOptions(theDGroup.m_SerOpts);
if(DialogResult.OK == _frmDlgGraphOptions.ShowDialog(this))
{
// Save the revised (or new) options to theDGroup
theDGroup.m_SerOpts = _frmDlgGraphOptions.Options;
}
This approach will force the user of your dialog to pass an initialization value or null.
However your InitSettings will work with a initialized value and you don't have initialized two times your options instance.
(Actually there isn't a big improvement from your code, but I think it is a better approach)
Your child class should probably have the m_SerOpts as a property:
public partial class DlgGraphOptions : Form
{
public GraphOpts_t m_SerOpts { get; set; }
}
Your click event can probably be cleaned up like this:
private void Item_Click(object sender, EventArgs e)
{
using (DlgGraphOptions _frmDlgGraphOptions = new DlgGraphOptions()) {
_frmDlgGraphOptions.m_SerOpts = theDGroup.m_SerOpts;
if (_frmDlgGraphOptions.ShowDialog(this) == DialogResult.OK)
{
// Save the revised options to the Data Group
theDGroup.m_SerOpts = _frmDlgGraphOptions.m_SerOpts;
}
}
}
where in your DlgGraphOptions form, you need to set the form's DialogResult property in the OK or Save button event.
You could also just pass m_SerOpts object through the constructor:
public partial class DlgGraphOptions : Form
{
public GraphOpts_t m_SerOpts { get; }
public DlgGraphOptions(GraphOpts_t serOpts) {
InitializeComponents();
m_SerOpts = serOpts;
}
}
I'm using MainWindow and Settings. MainWindow is the startup window from which I can open Settings. I'm trying to share some properties between both windows. Right now, I have the public properties declared in Settings:
public partial class Settings : Form
{
private string property1
public Settings()
{
InitializeComponent();
this.changeSettings();
}
public string property1
{
get { return property1; }
set { property1 = value; }
}
public void changeSettings()
{
textbox.Text = property1;
}
}
I can create an instance of Settings in MainWindow and change the properties from there:
public partial class Mainwindow : Form
{
private Settings settings;
public MainWindow()
{
InitializeComponent();
settings = new Settings();
this.changeSettings();
}
private void changeSettings()
{
settings.property1 = "value";
textbox.Text = settings.property1;
}
private void openSettings_Click(object sender, EventArgs e)
{
settings.ShowDialog();
}
}
Say, I want to change the contents of the textboxes in both forms. For MainWindow this works, i.e. I can store the value in the property and access it again. However, I open up Settings and try to change its textbox, the property is empty!
What could explain this?
You never called changeSettings() after setting the property.
You should probably get rid of that method and update the textbox directly in the setter.
The flaw is in Settings.property1, it doesn't update the text box that displays its value. A simple solution is:
public string property1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
You'll also need to update your MainWindow's text box after displaying the dialog:
private void openSettings_Click(object sender, EventArgs e)
{
settings.property1 = textbox.Text;
if (settings.ShowDialog() == DialogResult.OK) {
textbox.Text = settings.property1;
}
}