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();
}
Related
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));
};
I created a custom TimePicker and the renderers to android and iphone, with the objective to allow for that be nullable. As inspiration, was used the https://xamgirl.com/clearable-datepicker-in-xamarin-forms/
But, for some reason, the event is not firing when the time is set, thats happenen only in android, and more specific, back in android 8.1.
On shared project:
public class NullableTimePicker : TimePicker
{
public NullableTimePicker()
{
Time = DateTime.Now.TimeOfDay;
NullableTime = null;
Format = #"HH\:mm";
}
public string _originalFormat = null;
public static readonly BindableProperty PlaceHolderProperty =
BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(NullableTimePicker), " : ");
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProperty); }
set
{
SetValue(PlaceHolderProperty, value);
}
}
public static readonly BindableProperty NullableTimeProperty =
BindableProperty.Create(nameof(NullableTime), typeof(TimeSpan?), typeof(NullableTimePicker), null, defaultBindingMode: BindingMode.TwoWay);
public TimeSpan? NullableTime
{
get { return (TimeSpan?)GetValue(NullableTimeProperty); }
set { SetValue(NullableTimeProperty, value); UpdateTime(); }
}
private void UpdateTime()
{
if (NullableTime != null)
{
if (_originalFormat != null)
{
Format = _originalFormat;
}
}
else
{
Format = PlaceHolder;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext != null)
{
_originalFormat = Format;
UpdateTime();
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == TimeProperty.PropertyName ||
(
propertyName == IsFocusedProperty.PropertyName &&
!IsFocused &&
(Time == DateTime.Now.TimeOfDay)))
{
AssignValue();
}
if (propertyName == NullableTimeProperty.PropertyName && NullableTime.HasValue)
{
Time = NullableTime.Value;
if (Time == DateTime.Now.TimeOfDay)
{
//this code was done because when date selected is the actual date the"DateProperty" does not raise
UpdateTime();
}
}
}
public void CleanTime()
{
NullableTime = null;
UpdateTime();
}
public void AssignValue()
{
NullableTime = Time;
UpdateTime();
}
}
On Android project:
public class NullableTimePickerRenderer : ViewRenderer<NullableTimePicker, EditText>
{
public NullableTimePickerRenderer(Context context) : base(context)
{
}
TimePickerDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<NullableTimePicker> e)
{
base.OnElementChanged(e);
this.SetNativeControl(new Android.Widget.EditText(Context));
if (Control == null || e.NewElement == null)
return;
this.Control.Click += OnPickerClick;
if (Element.NullableTime.HasValue)
Control.Text = DateTime.Today.Add(Element.Time).ToString(Element.Format);
else
this.Control.Text = Element.PlaceHolder;
this.Control.KeyListener = null;
this.Control.FocusChange += OnPickerFocusChange;
this.Control.Enabled = Element.IsEnabled;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Xamarin.Forms.TimePicker.TimeProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.TimePicker.FormatProperty.PropertyName)
SetTime(Element.Time);
}
void OnPickerFocusChange(object sender, Android.Views.View.FocusChangeEventArgs e)
{
if (e.HasFocus)
{
ShowTimePicker();
}
}
protected override void Dispose(bool disposing)
{
if (Control != null)
{
this.Control.Click -= OnPickerClick;
this.Control.FocusChange -= OnPickerFocusChange;
if (_dialog != null)
{
_dialog.Hide();
_dialog.Dispose();
_dialog = null;
}
}
base.Dispose(disposing);
}
void OnPickerClick(object sender, EventArgs e)
{
ShowTimePicker();
}
void SetTime(TimeSpan time)
{
Control.Text = DateTime.Today.Add(time).ToString(Element.Format);
Element.Time = time;
}
private void ShowTimePicker()
{
CreateTimePickerDialog(this.Element.Time.Hours, this.Element.Time.Minutes);
_dialog.Show();
}
void CreateTimePickerDialog(int hours, int minutes)
{
NullableTimePicker view = Element;
_dialog = new TimePickerDialog(Context, (o, e) =>
{
view.Time = new TimeSpan(hours: e.HourOfDay, minutes: e.Minute, seconds: 0);
view.AssignValue();
((IElementController)view).SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control.ClearFocus();
_dialog = null;
}, hours, minutes, true);
_dialog.SetButton("ok", (sender, e) =>
{
SetTime(Element.Time);
this.Element.Format = this.Element._originalFormat;
this.Element.AssignValue();
});
_dialog.SetButton2("clear", (sender, e) =>
{
this.Element.CleanTime();
Control.Text = this.Element.Format;
});
}
}
On iOS project:
public class NullableTimePickerRenderer : TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
var timePicker = (UIDatePicker)Control.InputView;
timePicker.Locale = new NSLocale("no_nb");
if (e.NewElement != null && this.Control != null)
{
this.UpdateDoneButton();
this.AddClearButton();
this.Control.BorderStyle = UITextBorderStyle.Line;
Control.Layer.BorderColor = UIColor.LightGray.CGColor;
Control.Layer.BorderWidth = 1;
if (Device.Idiom == TargetIdiom.Tablet)
{
this.Control.Font = UIFont.SystemFontOfSize(25);
}
}
}
private void UpdateDoneButton()
{
var toolbar = (UIToolbar)Control.InputAccessoryView;
var doneBtn = toolbar.Items[1];
doneBtn.Clicked += (sender, args) =>
{
NullableTimePicker baseTimePicker = this.Element as NullableTimePicker;
if (!baseTimePicker.NullableTime.HasValue)
{
baseTimePicker.AssignValue();
}
};
}
private void AddClearButton()
{
var originalToolbar = this.Control.InputAccessoryView as UIToolbar;
if (originalToolbar != null && originalToolbar.Items.Length <= 2)
{
var clearButton = new UIBarButtonItem("clear", UIBarButtonItemStyle.Plain, ((sender, ev) =>
{
NullableTimePicker baseTimePicker = this.Element as NullableTimePicker;
this.Element.Unfocus();
this.Element.Time = DateTime.Now.TimeOfDay;
baseTimePicker.CleanTime();
}));
var newItems = new List<UIBarButtonItem>();
foreach (var item in originalToolbar.Items)
{
newItems.Add(item);
}
newItems.Insert(0, clearButton);
originalToolbar.Items = newItems.ToArray();
originalToolbar.SetNeedsDisplay();
}
}
}
This code works fine on iOS and Android version 8.1 or higher, but in lower version, this just not fire the event to set time, setting always the default time.
I'm also provided a git repo with the code, maybe, make easily understand my problem.
https://github.com/aismaniotto/Nullable24hTimePicker
Thanks for your help. After more digging on code and debugging, I realize the problem was on OkButton implementation. On Android 8, apparently, the callback from the TimePickerDialog was called before the OkButton implementation e what was there, is ignored. On the older version, the OKButton implementation was called before and, for some reason, cancel the invocation of the callback.
That way, removing the OkButton implementation, the problem was solved... remembering that on the working version, was ignored anyway. Eventually, I will commit to the repository, to be registered.
Thanks a lot.
I'm working on a project that requires me to create items in one form, put them in a List in another and them insert them for display on a list view on a third. I have a custom event set up to add the items to their appropriate place but it doesn't work and I can't figure out why.
My UserInputForm is where the info is put in. I have a get/set method to pull the data from the fields. The declared EventHandler is in this form.
public partial class UserInputForm : Form
{
public UserInputForm()
{
InitializeComponent();
}
MainForm main;
public EventHandler AddNewItem;
public Items EachItem
{
get
{
Items newItem = new Items();
newItem.Name = nameBox.Text;
newItem.Month = monthBox.Text;
newItem.Day = dayBox.Value;
newItem.Senior = seniorCheckBox.Checked;
newItem.ImageIndex = monthBox.SelectedIndex;
return newItem;
}
set
{
nameBox.Text = value.Name;
monthBox.Text = value.Month;
dayBox.Value = value.Day;
seniorCheckBox.Checked = value.Senior;
}
}
private void AddItem (object sender, EventArgs e)
{
if (main == null)
{
main = new MainForm();
}
AddNewItem += main.AddNewItemHandler;
if (AddNewItem != null)
{
AddNewItem(this, new EventArgs());
}
EachItem = new Items();
}
}
In the Main, I have the event that adds the item to the list, calls a method to display the # of items in the list, and calls a method in the ListView Form to add it there.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
List<Items> inputItems = new List<Items>();
UserInputForm newInput;
ListView list;
public void AddNewItemHandler(object sender, EventArgs e)
{
newInput = sender as UserInputForm;
Items newItem = newInput.EachItem;
inputItems.Add(newItem);
if (list == null)
{
list = new ListView();
}
list.AddToListView(newItem);
TotalInList();
}
private void OpenInputForm(object sender, EventArgs e)
{
newInput = new UserInputForm();
if (newInput.IsDisposed == true)
{
newInput = new UserInputForm();
}
newInput.Show();
OpenInputNum();
newInput.FormClosed += InputFormClosed;
}
private void OpenListView(object sender, EventArgs e)
{
if (Application.OpenForms.OfType<ListView>().Count<ListView>() == 1)
{
Application.OpenForms.OfType<ListView>().First().Close();
displayList.Checked = false;
}
else
{
if (list == null)
{
list = new ListView();
}
if (list.IsDisposed == true)
{
list = new ListView();
}
list.Show();
list.Load += ListOpened;
}
}
private void OpenInputNum()
{
string inputNum = $"{Application.OpenForms.OfType<UserInputForm>().Count<UserInputForm>()}";
openInputNumBox.Text = inputNum;
}
private void ListOpened(object sender, EventArgs e)
{
if (list == null)
{
list = new ListView();
}
if (list.IsDisposed == true)
{
list = new ListView();
}
list.AddToNewListView(inputItems);
}
public void TotalInList()
{
string listNum = $"{inputItems.Count()}";
itemListNumBox.Text = listNum;
}
}
The ListView Form has a method that adds all items at once when its opened or a single item when its already open. It changes the item to a ListViewItem and adds it to the list.
public partial class ListView : Form
{
public ListView()
{
InitializeComponent();
}
//list for listview
List<Items> toView;
public void AddToNewListView(List<Items> toListView)
{
toView = toListView;
ListViewItem addItem = new ListViewItem();
foreach (Items item in toView)
{
addItem.Text = item.ToString();
addItem.ImageIndex = item.ImageIndex;
addItem.Tag = item;
itemListView.Items.Add(addItem);
}
}
public void AddToListView(Items newItem)
{
ListViewItem addItem = new ListViewItem();
addItem.Text = newItem.ToString();
addItem.ImageIndex = newItem.ImageIndex;
addItem.Tag = newItem;
itemListView.Items.Add(addItem);
}
}
The problems I'm having are:
Though I follow the data and though it all seems to be there when needed, my ListView will not populate and neither will the count for the number of items in the main. Is there something wrong with my code?
I have 3 combobox ObjetivosCB, FrecuenciasCB and ResponsablesCB in my form as shows below
public partial class Form_Indicador : Form
{
public Indicador Indicador { get; set; }
private void Form_AgregarIndicador_Load(object sender, EventArgs e)
{
if (Indicador == null)
Indicador = new Indicador();
ConfigurarObjetivosCB();
ConfigurarFrecuenciasCB();
ConfigurarResponsablesCB();
CargarPropiedadesIndicador();
}
private void ConfigurarResponsablesCB()
{
ResponsableCB.DataSource = ResponsableRepository.Instance.All();
ResponsableCB.DisplayMember = "Area";
if (Indicador.Responsable == null)
ResponsableCB.SelectedIndex = -1;
}
private void ConfigurarFrecuenciasCB()
{
FrecuenciasCB.DisplayMember = "Periodo";
FrecuenciasCB.DataSource = IndicadorRepository.Instance.AllFrecuencias();
if (Indicador.Frecuencia == null)
FrecuenciasCB.SelectedIndex = -1;
}
private void ConfigurarObjetivosCB()
{
ObjetivosCB.DataSource = _objetivoFachada.All();
ObjetivosCB.DisplayMember = "Nombre";
if (Indicador.Objetivo == null) ObjetivosCB.SelectedIndex = -1;
}
private void CargarPropiedadesIndicador()
{
ObjetivosCB.DataBindings.Add("SelectedItem", Indicador, "Objetivo");
ResponsableCB.DataBindings.Add("SelectedItem", Indicador, "Responsable");
FrecuenciasCB.DataBindings.Add("SelectedItem", Indicador, "Frecuencia");
}
}
The problem is that FrecuenciasCB.SelectedItem and ResponsablesCB.SelectedItem always show and return the first item but ObjetivosCB.SelectedItem works fine. I am not understand... three methods has the same logic. What am I doing wrong?
I have been solved! The problem was a wrong definition for Equals() in Indicador and Frecuencia.
I'm studying design patterns right now, I'm fairly new to this model-view-presenter, although I have already experience in asp.net mvc I'm trying to do an implementation of mvp in winforms.
The string in the textbox will be sorted with an algorithm based on the combobox. Right now when I click the button it throws a null reference exception
Here is the UI:
Here are my classes and codes:
class FormPresenter
{
private ISortingView _view;
private string _algorithm;
private StringToSortModel sortMe = new StringToSortModel();
public FormPresenter(ISortingView view)
{
_view = view;
_view.sortTheString += view_sortString;
sortMe.sortThis = view.stringToSort;
_algorithm = _view.algorithm;
//Algorithm = view.stringToSort;
//sortingform.sortTheString += (obj
}
private void view_sortString(object sender, EventArgs e)
{
SortContext context = new SortContext();
_view.sortedText = context.Sort(sortMe.sortThis.ToCharArray());
}
}
interface ISortingView
{
event EventHandler sortTheString;
string stringToSort { get; }
string algorithm { get; }
string sortedText { get; set; }
}
public partial class SortingForm : Form, ISortingView
{
public SortingForm()
{
InitializeComponent();
comboBox1.Items.Add("Bubble Sort");
comboBox1.Items.Add("Insertion Sort");
comboBox1.SelectedItem = "Bubble Sort";
textBox1.Text = "Emiri";
}
public event EventHandler sortTheString;
public string algorithm { get { return comboBox1.SelectedItem.ToString(); } }
public string stringToSort { get { return textBox1.Text; } }
public string sortedText { get { return label2.Text; } set { label2.Text = value; } }
private void Form1_Load(object sender, EventArgs e)
{
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
//char[] x = textBox1.Text.ToCharArray();
//SortContext con = new SortContext();
//con.SetSortStrategy(new InsertionSort());
//label2.Text = con.Sort(x);
//if(sortString != null)
//{
//this prodcues a null exception error
sortTheString(sender, e);
//}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainForm = new SortingForm();
var presenter = new FormPresenter(mainForm);
Application.Run(new SortingForm());
}
}
I have not included the codes for the model and the classes the contains the sorting functions to keep this post short. The problem I have is that when button is clicked it throws a null reference exception error, something that I have been stuck on for hours already.
Sir/Ma'am your answers would be of great help. Thank you++
Your null is coming from this line
sortTheString(sender, e);
because you are not using the same form instance in your Presenter. Change to this in your main...
Application.Run(mainForm);
The event handler does not have any subscribers (because of the Application.Run(new SortingForm()); C# will treat that as null rather than an empty subscriber list.
ISortingView mainForm = new SortingForm();
var presenter = new FormPresenter(mainForm);
Application.Run(mainForm as Form);