Panel does not contain a constructor that takes 0 arguments - c#

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.

Related

I need to access a form control from another class (C#)

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.

Object loses data after initialization

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 :).

C# - Accessing a list of Objects from one class to another

I am having this very strange problem where i am creating a list of some objects in one class then trying to access it in another class but it's coming empty in other class:
My first class where i am populating the list:
namespace dragdrop
{
struct BR
{
private string var;
public string Var
{
get { return var; }
set { var = value; }
}
private string equalsTo;
public string EqualsTo
{
get { return equalsTo; }
set { equalsTo = value; }
}
private string output;
public string Output
{
get { return output; }
set { output = value; }
}
private string els;
public string Els
{
get { return els; }
set { els = value; }
}
private string elsOutput;
public string ElsOutput
{
get { return elsOutput; }
set { elsOutput = value; }
}
}
public partial class Form1 : Form
{
//******************
private List<BR> list = new List<BR>(); //This is the list!
//******************
internal List<BR> List
{
get { return list; }
set { list = value; }
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string[] vars = new string[] { "Name", "Gender", "Age", "Address", "email" };
comboBox1.DataSource = vars;
}
private void button1_Click(object sender, EventArgs e)
{
BR b = new BR();
b.Var = comboBox1.SelectedItem.ToString();
b.EqualsTo = textBox1.Text;
b.Output = textBox2.Text;
list.Add(b);
//*****************
textBox1.Text = List.Count.ToString(); //This gives the correct count value!
//*****************
//this.Close();
}
}
}
I am accessing it in second class like:
namespace dragdrop
{
public partial class Ribbon1
{
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
{
}
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Form1 form = new Form1();
List<BR> l = form.List; ;
//*******************
MessageBox.Show(form.List.Count.ToString()); //This strangely gives count 0!
//*******************
}
}
}
I have even tried making everything public in first class but no matter what i do, im always getting empty list in second class.
The is no relation what-so-ever between Form1 and Ribbon1, how can one then access an instance of the other?
With this:
Form1 form = new Form1(); // new instance of Form1
List<BR> l = form.List; ; // of course the list is empty in a new instance!
you can never access values from another instance of Form1.
Since I have no idea how your classes are connected I cannot give you more advice than have a look at this good overview of OO-relationships. You have to connect them somehow for it to work, I would very much recommend composition // aggregation (same thing, different schools).
All i needed to do was make the list a static member in class one, that solved the issue of having different value when i tried to create a new instance of Form1 in Ribbon1 class.
private static List<BR> list = new List<BR>();

MonoTouch Exception: Selector invoked from objective-c on a managed object of type that has been GC'ed

I'm getting this exception in my MonoTouch app and I can't seem to fix it. I've been trying for about 6 hours now and I'm having no luck.
My understanding of this exception is that an object is being referenced (or trying to be referenced) by MonoTouch but Garbage Collection has already disposed of it. Therefore it is looking to build a reference to it again using a constructor which I haven't set in the class, using a pointer.
Originally I thought that adding that constructor would be enough until I did some research and realised that at best, it would be a temporary bandage. What I'm finding strange is that as far as I can see I am holding a reference to everything. I've even gone a bit overboard and started creating member variables for things that don't necessarily need to be kept to try and capture the exception, but still nothing.
The error occurs when I load a ViewController, trigger a delegate which pushes a new ViewController onto the NavigationController (thus leaving the screen), hitting the back button and moving onto another view again. This consistently happens the same way and will always crash at this point, though not necessarily at the exact same time due to the way GC works I suppose.
The app is quite simple so far. The first screen loads 5 different charts using ShinobiCharts. Data Sources and Delegates are set in the ViewController and a ViewModel is sent to the View which then adds the charts to the subview. The exception occurs in ColumnChart (a custom made class inheriting from ShinobiChart). The exception will always be thrown on these controls never the View or the ViewController. However, which chart causes the exception is seemingly random every time.
How I'm storing the charts looks like this:
public class HomeViewModel
{
public ColumnChart MeetingsChart { get; set; }
public ColumnChart FirmDataChart { get; set; }
public ColumnChart SystemUseChart { get; set; }
public BarChart IllustrationsChart { get; set; }
public PieChart TermsOfBusinessChart { get; set; }
public ColumnChartDataSource MeetingsChartDataSource { get; set; }
public ColumnChartDataSource SystemUseChartDataSource { get; set; }
public StackChartDataSource FirmDataChartDataSource { get; set; }
public BarChartDataSource IllustrationsChartDataSource { get; set; }
public PieChartDataSource TermsOfBusinessDataSource { get; set; }
public BarChartDataProvider TermsOfBusinessDataProvider { get; set; }
public DashboardMeetingChartDelegate MeetingsChartDelegate { get; set; }
public ColumnChartDelegate SystemUseChartDelegate { get; set; }
public PieChartDelegate TermsOfBusinessDelegate { get; set; }
}
This class is then implemented in the Controller like this: (NOTE: Lots of code was stripped out as I didn't think it was needed)
public class HomeController : UIViewController
{
private HomeView _homeView;
private HomeViewModel _homeViewModel;
…..
public HomeController()
{
_homeViewModel = new HomeViewModel();
}
….
private void LoadCharts()
{
_homeViewModel.MeetingsChart = LoadMeetingsChart();
_homeViewModel.FirmDataChart = LoadFirmDataChart();
_homeViewModel.SystemUseChart = LoadSystemUseChart();
_homeViewModel.IllustrationsChart = LoadIllustrationsChart();
_homeViewModel.TermsOfBusinessChart = LoadTermsOfBusinessChart();
}
private ColumnChart LoadMeetingsChart()
{
ColumnChart meetingsChart = new ColumnChart(RectangleF.Empty);
meetingsChart.DataSource = _homeViewModel.MeetingsChartDataSource;
meetingsChart.Delegate = _homeViewModel.MeetingsChartDelegate;
meetingsChart.LicenseKey = LICENSE_KEY;
return meetingsChart;
}
}
Finally, the View:
public class HomeView : UIView
{
private HomeViewModel _homeViewModel;
public HomeView(RectangleF frame, UINavigationController navigationController, HomeViewModel homeViewModel)
: base(frame)
{
this._homeViewModel = homeViewModel;
this.BackgroundColor = UIColor.FromRGB(20, 20, 20);
this.Title = "Sales 360 Dashboard";
SetupChartBounds(UIApplication.SharedApplication.StatusBarOrientation);
}
public void SetupCharts()
{
SetupMeetingsChartHeaderBar();
SetupTermsOfBusinessChartHeaderBar();
SetupIllustrationsChartHeaderBar();
SetupSystemUserChartHeaderBar();
SetupFirmDataChartHeaderBar();
this.AddSubview(_homeViewModel.MeetingsChart);
this.AddSubview(_homeViewModel.TermsOfBusinessChart);
this.AddSubview(_homeViewModel.IllustrationsChart);
this.AddSubview(_homeViewModel.SystemUseChart);
this.AddSubview(_homeViewModel.FirmDataChart);
}
}
I'd really appreciate any help on this problem as I'm badly stuck on it. Thanks.
EDIT 1:
The HomeController is pushed from something called the SplashController. The only purpose of the SplashController is to display a splash screen view and call web services. The web services are called asynchronously and when completed will change the view to the home view where all charts are displayed.
Here is some of the SplashController class:
public class SplashController : UIViewController
{
private SplashView _splashView;
private SplashViewModel _splashViewModel;
private readonly string BACKGROUND_IMAGE_PATH;
private HomeController _homeController;
.....
public override void LoadView()
{
LoadViewModel();
_splashView = new SplashView(new RectangleF(0, 0, Dimensions.Width, Dimensions.Height), _splashViewModel);
this.View = _splashView;
}
// .... a number of service calls eventually leading to this
protected void GetTermsOfBusinessAllAgentsCompleted(object sender, GetTermsOfBusinessAllAgentsCompletedEventArgs e)
{
_servicesHelper.StopTimer(e.UserState as Timer);
if (e.Error != null)
HandleError(e.Error, "Terms of Business");
else
{
_termsOfBusinessGraphData = e.Result;
ChangeViewToHomeView();
}
}
private void ChangeViewToHomeView()
{
_homeController = new HomeController(_meetingsGraphData, _firmDataGraphData, _systemUseGraphData,
_illustrationsGraphData, _termsOfBusinessGraphData);
this.NavigationController.PushViewController(_homeController, false);
}
}
The HomeView is created in the LoadView method overridden in the HomeController class and basically looks like this:
_homeView = new HomeView(new RectangleF(0, 0, Dimensions.Width, Dimensions.Height), this.NavigationController, _homeViewModel);
this.View = _homeView;
As for the NavigationController, good question. This was something I was doing earlier where I was passing a NavigationController to a delegate. However, now all of this is done in the HomeController so it is no longer needed. Sorry about that, my mistake! I have now taken this out and tried re-running the app, the same thing still happens. Please ignore this bit.
EDIT 2:
Here is the delegate for one of the charts. I've missed out the methods which handles errors as it's not needed. The _meetingServices.GetAgentsData method calls a web service in that method which then returns to the event in this object:
public class DashboardMeetingChartDelegate : SChartDelegate
{
private UINavigationController _navigationController;
private CategoryGraph _meetingsGraphData;
private MeetingServices _meetingServices;
private ServicesHelper _servicesHelper;
private int _currentIndex;
public DashboardMeetingChartDelegate(UINavigationController navigationController, CategoryGraph meetingsGraphData)
{
this._navigationController = navigationController;
this._meetingsGraphData = meetingsGraphData;
_meetingServices = new MeetingServices();
_servicesHelper = new ServicesHelper();
_currentIndex = -1;
}
protected override void OnToggledSelection (ShinobiChart chart, SChartDataPoint dataPoint, SChartSeries series, PointF pixelPoint)
{
_currentIndex = dataPoint.Index;
_meetingServices.GetAgentsData(GetAgentsCompleted);
}
protected void GetAgentsCompleted(object sender, GetAgentsCompletedEventArgs e)
{
_servicesHelper.StopTimer(e.UserState as Timer);
if (e.Error != null)
HandleError(e.Error);
else
{
int rsmId = Convert.ToInt32(_meetingsGraphData.Data[0].SeriesDataPoints[_currentIndex].PointMetaData);
AgentContract currentAgent = e.Result.Agents.Where(agent => agent.Id == rsmId).First();
_navigationController.PushViewController(new MeetingController(currentAgent), true);
}
}
And here is the MeetingController - pretty similar to the other one except there's less to load:
public class MeetingController : UIViewController
{
private AgentContract _currentAgent;
private RsmChartView _meetingView;
private RsmChartViewModel _meetingViewModel;
private MeetingTypeController _meetingTypeController;
private ColumnChartDataProvider _columnChartDataProvider;
public MeetingController(AgentContract currentAgent)
{
_currentAgent = currentAgent;
}
public override void LoadView ()
{
LoadColumnChartDataProvider();
LoadViewModel();
InitialiseView();
}
private void LoadColumnChartDataProvider()
{
_columnChartDataProvider = new ColumnChartDataProvider();
_columnChartDataProvider.XValueList.Add(new NSString("Phone"));
_columnChartDataProvider.XValueList.Add(new NSString("Demo"));
_columnChartDataProvider.XValueList.Add(new NSString("Fact Finding"));
_columnChartDataProvider.YValueList.Add(61);
_columnChartDataProvider.YValueList.Add(22);
_columnChartDataProvider.YValueList.Add(27);
}
private void LoadViewModel()
{
_meetingViewModel = new RsmChartViewModel();
_meetingViewModel.Chart = LoadMeetingChart();
_meetingViewModel.RsmContentModel = LoadRsmContentModel();
}
private RsmContentModel LoadRsmContentModel()
{
RsmContentModel model = new RsmContentModel();
model.Name = _currentAgent.Name;
return model;
}
private void InitialiseView()
{
_meetingView = new RsmChartView(new RectangleF(0, 0, Dimensions.Width, Dimensions.Height), _meetingViewModel);
this.View = _meetingView;
}
private ColumnChart LoadMeetingChart()
{
ColumnChart meetingChart = new ColumnChart(new RectangleF(10, 220, Dimensions.Width - 20, 540));
_meetingTypeController = new MeetingTypeController();
_meetingViewModel.ChartDataSource = new ColumnChartDataSource(_columnChartDataProvider);
_meetingViewModel.ChartDelegate = new ColumnChartDelegate(this.NavigationController, _meetingTypeController);
meetingChart.DataSource = _meetingViewModel.ChartDataSource;
meetingChart.Delegate = _meetingViewModel.ChartDelegate;
return meetingChart;
}
public override void ViewDidLoad()
{
_meetingView.Load();
}
}
This condition is often hard to debug. Some changes have been made to MonoTouch to reduce such occurrences.
To try them I suggest you to try 6.0.7 (on the beta channel) and report any outstanding issue on the mailing list (or a bug report).
More details can be found in the release notes (direct link).

Display specific properties in the Property grid

When we press the btnSettings, all the user controls properties will be displayed in Property grid. I want display specific properties (only TemperatureValue and TemperatureUnit), is possible? User control code as follows:
using System;
using System.Windows.Forms;
namespace Temperature
{
public partial class temperatureUc : UserControl
{
public enum temperatureUnit
{
Celsius, // default
Delisle, // °De = (100 − °C) * 3⁄2
Fahrenheit, // °F = °C * 9⁄5 + 32
Kelvin, // °K = °C + 273.15
Newton, // °N = °C * 33⁄100
Rankine, // °R = (°C + 273.15) * 9⁄5
Réaumur, // °Ré = °C * 4⁄5
Rømer // °Rø = °C * 21⁄40 + 7.5
}
public temperatureUc()
{
InitializeComponent();
this.cboTemperatureUnit.DataSource = Enum.GetValues(typeof(temperatureUnit));
}
#region "Event"
public delegate void SettingsStateEventHandler(object sender, EventArgs e);
public event SettingsStateEventHandler settingsStateChanged;
private void OnSettingsChanged(object sender, EventArgs e)
{
if (this.settingsStateChanged != null)
this.settingsStateChanged(sender, e);
}
#endregion
#region "Properties"
private Single _TemperatureValue;
public Single TemperatureValue
{
get
{
return this._TemperatureValue;
}
set
{
if (value.GetType() == typeof(Single))
{
_TemperatureValue = value;
this.txtTemperatureValue.Text = _TemperatureValue.ToString();
}
}
}
private temperatureUnit _TemperatureUnit;
public temperatureUnit TemperatureUnit
{
get
{
return this._TemperatureUnit;
}
set
{
if (value.GetType() == typeof(temperatureUnit))
{
_TemperatureUnit = value;
this.cboTemperatureUnit.Text = _TemperatureUnit.ToString();
}
}
}
#endregion
private void btnSettings_Click(object sender, EventArgs e)
{
this.OnSettingsChanged(sender, e);
}
}
}
User control above code will be called from code bellow:
using System;
using System.Windows.Forms;
using Temperature;
using System.Diagnostics;
using System.Drawing;
namespace TemperatureImplements
{
public partial class Form1 : Form
{
private PropertyGrid pGrid = new PropertyGrid();
public Form1()
{
InitializeComponent();
this.temperatureUc1.settingsStateChanged += new temperatureUc.SettingsStateEventHandler(temperatureUc1_settingsStateChanged);
}
void temperatureUc1_settingsStateChanged(object sender, EventArgs e)
{
pGrid.Size = new Size(300, 500);
pGrid.Location = new Point(300,10);
pGrid.SelectedObject = temperatureUc1;
this.Controls.Add(pGrid);
}
}
}
Picture as follows:
There is a way. This article has a section called "Customizing the PropertyGrid Control" that explains how to do it http://msdn.microsoft.com/en-us/library/aa302326.aspx#usingpropgrid_topic5
Basically you just want to define the AppSettings class to only include TemperatureUnit andTemeratureValue`.
AppSettings appset = new AppSettings();
MyPropertyGrid.SelectedObject = appset;
Define AppSettings as follows;
[DefaultPropertyAttribute("SaveOnClose")]
public class AppSettings{
private bool saveOnClose = true;
private string tempUnit;
private int tempValue;
[CategoryAttribute("Global Settings"),
ReadOnlyAttribute(false),
DefaultValueAttribute("Celsius")]
public string TemperatureUnit
{
get { return tempUnit; }
set { tempUnit = value; }
}
[CategoryAttribute("Global Settings"),
ReadOnlyAttribute(false),
DefaultValueAttribute(0)]
public string TemperatureValue
{
get { return tempValue; }
set { tempValue = value; }
}
}
By the way, I'm changing the category from Misc to Global Settings, don't know if that's what you want but it makes sense when they're the only options. You may have to explicitly declare the other attributes this BrowsableAttribute(false) so they're not displayed but I don't think it's necessary.
There might be a way to hide those properties but I think that's the wrong way to go about it.
Instead of passing the user control itself you should create a model with TemperatureUnit and TemperatureValue. Move your defined events to this model.
Then you need to extend a user control which you pass the model to and listens for these events.
Finally set pGrid.SelectedObject to your model and you'll be good to go.

Categories