I am working on a Xamarin.Forms app that will store a list of receipes for some chemicals. The chemicals have names (not user changeable) and concentrations. I want the user to be able to change the concentrations in the form and store them.
I have it to where I can modify values in the list with the UPDATE button, I can add (or delete) members of the list, all persistent. However, I cannot figure out how to change the values in the list within the Entry box, itself.
I tried to do something like Arvind Chourasiya did here, but I could not quite figure out the equivalent "connection.Update." I think the SQLite can't be connected to because it's not in the right event, but I am not sure.
My most functional C# code is...
using System;
using System.ComponentModel;
using Xamarin.Forms;
using SQLite;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
namespace contactBook
{
public class Recipe : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
private string _name;
[MaxLength(255)]
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
OnPropertyChanged();
}
}
private double _concentration;
public double Concentration
{
get
{ return _concentration; }
set
{
if (_concentration == value)
return;
_concentration = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// Learn more about making custom code visible in the Xamarin.Forms previewer
// by visiting https://aka.ms/xamarinforms-previewer
[DesignTimeVisible(false)]
public partial class MainPage : ContentPage
{
private SQLiteAsyncConnection _connection;
private ObservableCollection<Recipe> _recipes;
public MainPage()
{
InitializeComponent();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
//setBasicReceipies();
}
protected override async void OnAppearing()
{
await _connection.CreateTableAsync<Recipe>();
var recipes = await _connection.Table<Recipe>().ToListAsync();
_recipes = new ObservableCollection<Recipe>(recipes);
recipesListView.ItemsSource = _recipes;
base.OnAppearing();
}
async void setBasicReceipies() // worked during tests
{
var recipe1 = new Recipe { Name = "NH3", Concentration = 0.0 };
var recipe2 = new Recipe { Name = "H2SO4", Concentration = 0.1 };
var recipe3 = new Recipe { Name = "NaCl", Concentration = 0.2 };
await _connection.InsertAsync(recipe1);
await _connection.InsertAsync(recipe2);
await _connection.InsertAsync(recipe3);
}
async void OnAdd(object sender, System.EventArgs e)
{
var recipe = new Recipe { Name = "test ", Concentration = 0.0 };
await _connection.InsertAsync(recipe);
_recipes.Add(recipe);
}
async void OnUpdate(object sender, System.EventArgs e)
{
var recipe = _recipes[0];
recipe.Concentration += 0.05;
await _connection.UpdateAsync(recipe);
}
async void OnDelete(object sender, System.EventArgs e)
{
var recipe = _recipes[0];
await _connection.DeleteAsync(recipe);
_recipes.Remove(recipe);
}
//async void Entry_PropertyChanged(System.Object sender, System.ComponentModel.PropertyChangedEventArgs e)
//{
// await _connection.UpdateAllAsync();
//}
}
}
Related
I got a datagridview and per Button I can Add items, and now I want to see in the datagridview that something changed.
I tried so many thing. this.Refresh(); this.Invalidate(); nothing works,
I found Application.Restart(); but I am not happy with this solution.
Is there anything else??
var contex = new frachtkostenEntities();
try
{
var x = new Kunden()
{
Kundenname = first,
Zielort = second,
};
contex.Kunden.Add(x);
contex.SaveChanges();
}
Now the new Items are in the Database, but the datagridview stays the same
This is the code to display the database
frachtkostenEntities context = new frachtkostenEntities();
var item = from p in context.Artikel
select new
{
Artikelnummer = p.Artikelnummer,
Bezeichnung = p.Bezeichnung,
};
dgvDisplayDataBase.DataSource = item.ToList();
your problem is that new data has not been uploaded, you can create a function
void LoadData()
{
//code load data from db
}
Then add code to the event AddButton
void button1_Click(object sender, EventArgs e)
{
//code add new record
LoadData();
}
There is no reason to refresh, reload or invalidate when setup properly as per below.
To see immediate change when adding a new item is to setup with a BindingList and a BindingSource. Then add the new item to the BindingList.
Also consider using INotifyPropertyChanged for when a property value changes in the model.
Using the following model
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
using North.Interfaces;
namespace North.Models
{
public partial class Contacts : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
private int? _contactTypeIdentifier;
public int Id => ContactId;
public int ContactId { get; set; }
public string FirstName
{
get => _firstName;
set
{
_firstName = value;
OnPropertyChanged();
}
}
public string LastName
{
get => _lastName;
set
{
_lastName = value;
OnPropertyChanged();
}
}
public int? ContactTypeIdentifier
{
get => _contactTypeIdentifier;
set
{
_contactTypeIdentifier = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then in the form, load data to the BindingList, add button adds a new record and shows the new primary key.
namespace North.Forms
{
public partial class ContactAddForm : Form
{
private BindingList<Contacts> _bindingList;
private readonly BindingSource _bindingSource = new BindingSource();
public ContactAddForm()
{
InitializeComponent();
dataGridView1.AutoGenerateColumns = false;
Shown += OnShown;
}
private void OnShown(object sender, EventArgs e)
{
using (var context = new NorthwindContext())
{
_bindingList = new BindingList<Contacts>(context.Contacts.ToList());
_bindingSource.DataSource = _bindingList;
dataGridView1.DataSource = _bindingSource;
}
_bindingSource.MoveLast();
}
private void AddNewContactButton_Click(object sender, EventArgs e)
{
// hard coded contact
var newContact = new Contacts()
{
FirstName = "Karen",
LastName = "Payne",
ContactTypeIdentifier = 1
};
// add to list and display in DataGridView
_bindingList.Add(newContact);
// save changes
using (var context = new NorthwindContext())
{
context.Add(newContact).State = EntityState.Added;
context.SaveChanges();
}
// See new primary key
MessageBox.Show("Id " + newContact.ContactId.ToString());
}
}
}
Edit
To see values of the current row in the DataGridView
private void CurrentContactButton_Click(object sender, EventArgs e)
{
Contacts current = _bindingList[_bindingSource.Position];
MessageBox.Show($"{current.ContactId}, {current.FirstName}, {current.LastName}");
}
I am tying to further understand MVVM with some example scenario. I have a rootpage with a 'maindisplay' textblock. I would like to display 'status' or 'scenarios' from activation of any form of UI eg. togglebutton on the 'maindisplay' textblock.
I am able to bind the the page navigation info in the rootpageviewmodel to the textblock. However, I am not able to achieve the result when displaying info from different page.
I have checked another post multiple-viewmodels-in-same-view & Accessing a property in one ViewModel from another it's quite similar but it didn't work.
Please help. Thanks.
While accessing the RootPageViewModel should retain the instance?
View
<TextBlock Text="{x:Bind RootViewModel.MainStatusContent, Mode=OneWay}"/>
RootPage.xaml.cs
public sealed partial class RootPage : Page
{
private static RootPage instance;
public RootPageViewModel RootViewModel { get; set; }
public RootPage()
{
RootViewModel = new RootPageViewModel();
this.InitializeComponent();
// Always use the cached page
this.NavigationCacheMode = NavigationCacheMode.Required;
}
public static RootPage Instance
{
get
{
if (instance == null)
{
instance = new RootPage();
}
return instance;
}
}
private void nvTopLevelNav_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
contentFrame.Navigate(typeof(SettingsPage));
RootViewModel.MainStatusContent = "Settings_Page";
}
else
{
var navItemTag = args.InvokedItemContainer.Tag.ToString();
RootViewModel.MainStatusContent = navItemTag;
switch (navItemTag)
{
case "Home_Page":
contentFrame.Navigate(typeof(HomePage));
break;
case "Message_Page":
contentFrame.Navigate(typeof(MessagePage));
break;
}
}
}
}
RootPage ViewModel:
public class RootPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private static RootPageViewModel instance = new RootPageViewModel();
public static RootPageViewModel Instance
{
get
{
if (instance == null)
instance = new RootPageViewModel();
return instance;
}
}
public RootPageViewModel()
{
}
private string _mainStatusContent;
public string MainStatusContent
{
get
{
return _mainStatusContent;
}
set
{
_mainStatusContent = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
MessagePage.xaml.cs - to access RootPage ViewModel
public sealed partial class MessagePage : Page
{
public MessagePageViewModel MessageViewModel { get; set; }
public MessagePage()
{
MessageViewModel = new MessagePageViewModel();
this.InitializeComponent();
// Always use the cached page
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private void Message1_Checked(object sender, RoutedEventArgs e)
{
RootPageViewModel.Instance.MainStatusContent = "Message 1 Selected";
}
private void Message1_Unchecked(object sender, RoutedEventArgs e)
{
RootPageViewModel.Instance.MainStatusContent = "Message 1 De-Selected";
}
}
When I debug the value did write to the instance but did't update the TextBlock. Did I do anything wrong in my XAML binding?
UWP C# MVVM How To Access ViewModel from Other Page
The better way is make static variable for RootPage, but not make singleton instance for RootPage and RootPageViewModel.
For example:
public RootPage ()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
Instance = this;
RootViewModel = new RootPageViewModel();
}
public static RootPage Instance;
Usage
private void Message1_Checked(object sender, RoutedEventArgs e)
{
RootPage.Instance.RootViewModel.MainStatusContent = "Message 1 Selected";
}
private void Message1_Unchecked(object sender, RoutedEventArgs e)
{
RootPage.Instance.RootViewModel.MainStatusContent = "Message 1 De-Selected";
}
Here is code I have been working on with the help of Deczalof:
Here is the XAML code that I have
<t:PopupEntryFrame2 x:Name="newDeckNameEntry" TextChanged="newDeckNameEntry_TextChanged" />
With code behind:
public partial class CopyDeckPopup : Rg.Plugins.Popup.Pages.PopupPage
{
string originalName;
string originalDescription;
List<Deck> listDecks;
public CopyDeckPopup(string clickedDeckName, string clickedDeckDescription)
{
InitializeComponent();
listDecks = App.DB.GetAllDecks();
newDeckNameEntry.Text = clickedDeckName;
newDeckDescriptionEntry.Text = clickedDeckDescription;
originalName = clickedDeckName;
originalDescription = clickedDeckDescription;
OK_Button.IsEnabled = false;
}
private async void Cancel_Button_Clicked(object sender, EventArgs e)
{
await PopupNavigation.Instance.PopAsync(false);
}
private async void OK_Button_Clicked(object sender, EventArgs e)
{
if (IsBusy)
return;
IsBusy = true;
await PopupNavigation.Instance.PopAsync(false);
var newDeckNameEntryTextTrim = newDeckNameEntry.Text.Trim();
var newDeckDescriptionEntryTextTrim = newDeckDescriptionEntry.Text.Trim();
if (newDeckNameEntryTextTrim != originalName || newDeckDescriptionEntryTextTrim != originalDescription)
{
App.DB.CopyDeckToDb2(originalName, newDeckNameEntryTextTrim, newDeckDescriptionEntryTextTrim);
MessagingCenter.Send<PopupPage>(new PopupPage(), "PageRefresh");
}
IsBusy = false;
}
void newDeckNameEntry_TextChanged(object sender, EntryTextChangedEventArgs e)
{
NewDeckNameEntryValidator(e.NewTextValue);
}
void newDeckDescriptionEntry_TextChanged(object sender, EntryTextChangedEventArgs e)
{
var deckName = newDeckNameEntry.Text.Trim();
var isDeckAvailable = listDecks.Where(x => x.Name == deckName).SingleOrDefault();
if (isDeckAvailable == null)
{
OK_Button.IsEnabled = e.NewTextValue != originalDescription ? true : false;
}
}
void NewDeckNameEntryValidator(string newDeckNameEntry)
{
var newDeckNameEntryTrimmed = newDeckNameEntry.Trim();
var isDeckNameAvailable = listDecks.Where(x => x.Name == newDeckNameEntryTrimmed).SingleOrDefault();
if (string.IsNullOrWhiteSpace(newDeckNameEntryTrimmed) ||
isDeckNameAvailable != null ||
newDeckNameEntryTrimmed.StartsWith("::") ||
newDeckNameEntryTrimmed.EndsWith("::") ||
newDeckNameEntryTrimmed.Count(c => c == ':') > 2)
{
OK_Button.IsEnabled = false;
return;
}
OK_Button.IsEnabled = true;
}
}
and the C# code for a template:
public class PopupEntryFrame2 : CustomFrame
{
CustomEntry entry { get; set; }
public PopupEntryFrame2()
{
entry = new CustomEntry();
entry.SetBinding(PopupEntryFrame2.TextProperty, new Binding("Text", source: this));
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs(a.NewTextValue, a.OldTextValue));
};
Content = entry;
CornerRadius = 5;
HasShadow = false;
SetDynamicResource(BackgroundColorProperty, "EntryFrameBackgroundColor");
SetDynamicResource(BorderColorProperty, "EntryFrameBorderColor");
SetDynamicResource(CornerRadiusProperty, "EntryFrameCornerRadius");
SetDynamicResource(HeightRequestProperty, "PopupEntryFrameHeight");
SetDynamicResource(MarginProperty, "PopupEntryFrameMargin");
SetDynamicResource(PaddingProperty, "PopupEntryFramePadding");
}
public class EntryTextChangedEventArgs : EventArgs
{
public EntryTextChangedEventArgs(String newValue = null, String oldValue = null)
{
NewTextValue = newValue;
OldTextValue = oldValue;
}
public String NewTextValue { get; }
public String OldTextValue { get; }
}
public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
TextChanged?.Invoke(this, args);
}
public static readonly BindableProperty TextProperty =
BindableProperty.Create(nameof(Text), typeof(string), typeof(PopupEntryFrame2), default(string));
public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
}
The error I get when building is this:
CopyDeckPopup.xaml(22,63): XamlC error XFC0002: EventHandler "newDeckNameEntry_TextChanged"
with correct signature not found in type "DecksTab.Pages.DeckOptions.CopyDeckPopup"
To achieve your goal you can simply add the Entry on your PopupEntryFrame class and define an Event there that connects with the TextChanged event in the original Entry.
This is done as illustrated in the code below (which is based on yours!)
using Test.Renderers;
namespace Test.Templates
{
public class PopupEntryFrame : CustomFrame
{
Entry entry { get; set; }
public PopupEntryFrame()
{
entry = new Entry();
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs());
};
Content = entry;
CornerRadius = 5;
HasShadow = false;
SetDynamicResource(BackgroundColorProperty, "EntryFrameBackgroundColor");
SetDynamicResource(BorderColorProperty, "EntryFrameBorderColor");
SetDynamicResource(CornerRadiusProperty, "EntryFrameCornerRadius");
SetDynamicResource(HeightRequestProperty, "PopupEntryFrameHeight");
SetDynamicResource(MarginProperty, "PopupEntryFrameMargin");
SetDynamicResource(PaddingProperty, "PopupEntryFramePadding");
}
public class EntryTextChangedEventArgs : EventArgs
{
// class members
}
public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
TextChanged?.Invoke(this, args);
}
}
}
And that's it. By doing that you can now write code like
<t:PopupEntry x:Name="newDeckDescriptionEntry" TextChanged="newDeckDescriptionEntry_TextChanged">
Update
In the comments someone suggested using ContentView, so let's take a look at how the same result could be achieved using that approach.
Disclaimer
First of all, it is important to know that Frame inherits itself from ContentView (from which acctualy it inherits its Content property!). In fact, from the documentation we know that
[Xamarin.Forms.ContentProperty("Content")]
[Xamarin.Forms.RenderWith(typeof(Xamarin.Forms.Platform._FrameRenderer))]
public class Frame : Xamarin.Forms.ContentView, Xamarin.Forms.IBorderElement, Xamarin.Forms.IElementConfiguration<Xamarin.Forms.Frame>
which means that by creating a Class/Control that inherits from Frame means that we are already using the ContentView approach.
Create the ContentView
First of all we create a ContentView and set its content to a new PopupFrame() which itself contains an Entry, as follows
public class PopupEntry : ContentView
{
Entry entry { get; set; }
public PopupEntry()
{
entry = new Entry();
Content = new PopupFrame()
{
Content = entry
};
}
}
Add an Event
Next, as is required by the OP, we define an Event for our ContentView that will be triggered when the Text in the Entry changed. Following the Documentation, this can be achieved by adding the following piece of code:
public class EntryTextChangedEventArgs : EventArgs
{
// class members
}
public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
TextChanged?.Invoke(this, args);
}
Now, we can "link" the original TextChanged event from the Entry control to the new Event of our ContentView, as follows:
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs());
};
Then, our ContentView code will look like
public class PopupEntry : ContentView
{
Entry entry { get; set; }
public PopupEntry()
{
entry = new Entry();
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs());
};
Content = new PopupFrame()
{
Content = entry
};
}
public class EntryTextChangedEventArgs : EventArgs
{
// class members
}
public event EventHandler TextChanged;
protected virtual void OnTextChanged(EntryTextChangedEventArgs args)
{
TextChanged?.Invoke(this, args);
}
}
Wrapping up
With this ContentView defined, we can now write code like
<t:PopupEntry x:Name="newDeckDescriptionEntry" TextChanged="newDeckDescriptionEntry_TextChanged"/>
And that's it! I hope this was useful.
Happy coding!
P.S.:
A little note about the Event declaration: Since EntryTextChangedEventArgs is a copy of the original TextChangedEventArgs we can define the EntryTextChangedEventArgs class like
public class EntryTextChangedEventArgs : EventArgs
{
public EntryTextChangedEventArgs(String newValue = null, String oldValue = null)
{
NewTextValue = newValue;
OldTextValue = oldValue;
}
public String NewTextValue { get; }
public String OldTextValue { get; }
}
and then when instantiating this class we just feed it directly with the values from TextChangedEventArgs, as follows
entry = new Entry();
entry.TextChanged += (s, a) =>
{
OnTextChanged(new EntryTextChangedEventArgs(a.NewTextValue, a.OldTextValue));
};
This works (the graph is properly loaded):
var oxyPlotView = new OxyPlotView{ Model = GetPlotModelSynch() };
This doesn't (the graph remains empty):
var oxyPlotView = new OxyPlotView();
// Here PlotModel will be loaded asynchronously from the BindingContext:
oxyPlotView.SetBinding(OxyPlotView.ModelProperty, new Binding(nameof(GraphViewModel.PlotModel)));
I have made proper isolated tests to ensure that INotifyPropertyChanged is working properly with my ViewModel. So the problem seems to be that OxyPlotView is built properly only if it has al the info from its inception (?). Is that even possible?
Here is the full ViewModel. INotifyPropertyChanged works because Title is behaving as intended (Title is binded to a Label in the same view).
class GraphViewModel : INotifyPropertyChanged
{
IGraphSeriesGroupRepository _graphSeriesGroupRepository;
private GraphSeriesGroup _graphSeriesGroup;
private ulong _sensorId;
public event PropertyChangedEventHandler PropertyChanged;
private PlotModel _plotModel;
public PlotModel PlotModel
{
get { return _plotModel; }
set
{
if (_plotModel != value)
{
_plotModel = value;
OnPropertyChanged(nameof(PlotModel));
}
}
}
private string _title;
public string Title
{
get { return _title; }
set
{
if (_title != value)
{
_title = value;
OnPropertyChanged(nameof(Title));
}
}
}
private bool _isLoading;
public bool IsLoading
{
get { return _isLoading; }
set
{
_isLoading = value;
OnPropertyChanged(nameof(IsLoading));
}
}
public GraphViewModel(IGraphSeriesGroupRepository graphSeriesGroupRepository, ulong sensorId)
{
_graphSeriesGroupRepository = graphSeriesGroupRepository;
_sensorId = sensorId;
Load();
}
public PlotModel GetPlotModelSynch()
{
_graphSeriesGroup = _graphSeriesGroupRepository.GetGraphSeriesGroup(_sensorId);
return GetPlotModel(_graphSeriesGroup);
}
private async void Load()
{
IsLoading = true;
await Task.Delay(5000);
_graphSeriesGroup = await _graphSeriesGroupRepository.GetGraphSeriesGroupAsync(_sensorId);
ApplyChanges();
IsLoading = false;
}
private void ApplyChanges()
{
// ---
Title = _graphSeriesGroup.Title;
PlotModel = GetPlotModel(_graphSeriesGroup);
}
private PlotModel GetPlotModel(GraphSeriesGroup graphSeriesGroup)
{
...
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Update: The only way I've found to make it work is:
private void chatter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == nameof(GraphViewModel.PlotModel))
{
_oxyPlotView = new OxyPlotView
{
Model = _graphViewModel.PlotModel
};
_stackLayout.Children.Add(_oxyPlotView);
}
}
...even updating an _oxyPlotView (which was already added to the StackLayout) and calling _oxyPlotView.InvalidateDisplay() didn't work.
I have a UserControl and a class for updating/storing results in a database. I need to automatically refresh the UserControlResultDislpay upon storing data. I have created and event to trigger a refresh when an Update occurs. I have the following code:
Class InstrumentTest:
public delegate void UpdateResultDisplay(object sender, EventArgs e);
public event UpdateResultDisplay RefreshDisplay;
protected virtual void OnNewResult(EventArgs e)
{
if (RefreshDisplay != null)
RefreshDisplay(this, e);
}
public void UpdateResultDB(ResultDataJFTOT resultData)
{
AnalysisListCommon myresult = PContext.GetInstance().DbHandlerLocal.StoredResult(
resultData.SampleId,
resultData.TestDate.ToString("yyyy-MM-ddTHH:mm", CultureInfo.InvariantCulture),
resultData.InstrumentSn,
StringRepository.constStringSampleName);
if (myresult != null)
{
Result r = new Result(new Guid(myresult.ResultId));
ResultData rd = r.GetResultData("Rating", FindResultDataMode.byVariableIdentifier);
string xmlTubeRating = resultData.tRating.ToString().Replace("#LT#", "<");
rd.Text = xmlRating;
rd.Store();
rd = r.GetResultData("TestDate", FindResultDataMode.byVariableIdentifier);
rd.Text = resultData.Date.ToString();
rd.Store();
OnNewResult(EventArgs.Empty);
}
else
{
AddTestToQueue(resultData);
}
}
public static InstrumentTest Instance()
{
//If instance is null create a new instance of the InstrumentTest
if (instrumentTestInstance == null)
{
instrumentTestInstance = new InstrumentTest();
}
return instrumentTestInstance;
}
Code from UserControl:
public UserControlResultDisplay()
{
this.InitializeComponent();
this.InitializeUIStrings();
this.InitializePlot();
EventListener(resultChanged);
}
private InstrumentTest resultChanged = InstrumentTest.Instance();
public void EventListener(InstrumentTest resultChanged)
{
//resultChanged = (InstrumentTest)obj;
resultChanged.RefreshDisplay += DisplayNewResultData;
}
private void DisplayNewResultData(object sender, EventArgs e)
{
RefreshCurrentResult();
}