I want to give an out parameter on event but it doesn't work
like this
error :Cannot use parameter out in anonyme method or lambda expression
public void CallMyMethod()
{
//{... code here`removed...just for initialize object}
int? count = null;
MyMethod(myObject,count);
}
public static void MyMethod(AnObject myObject,out int?count)
{
//{... code removed...}
IEnumerable<AnAnotherObject> objects = myObject.GetAllObjects();//... get objects
count = (count == null) ? objects.Count() : count;
MyPopup popup = CreateMypopup();
popup.Show();
popup.OnPopupClosed += (o, e) =>//RoutedEventHandler
{
if (--count <= 0)
{
Finished();//method to finish the reccursive method;
}
else
{
MyMethod(myObject, out count);
}
};
}
The standard way to pass parameters to event handler is to define your own class
public class MyObjectArgs: EventArgs
{
MyObject myObj {get;set}
int? Count {get; set;}
}
and then pass the same instance of this class down the recursive calls
public void CallMyMethod()
{
//{... code here`removed...just for initialize object}
MyObjectArgs args = new MyObjectArgs();
args.Count = null;
args.myOby = myObject;
MyMethod(args);
}
public static void MyMethod(MyObjectArgs args)
{
//{... code removed...}
IEnumerable<AnAnotherObject> objects = args.myObj.GetAllObjects();//... get objects
args.Count = (args.Count == null) ? objects.Count() : args.Count;
MyPopup popup = CreateMypopup();
popup.Show();
popup.OnPopupClosed += (o, e) =>//RoutedEventHandler
{
if (--args.Count <= 0)
{
Finished();//method to finish the reccursive method;
}
else
{
MyMethod(args);
}
};
}
Also the line where you count the objects seems to be incorrect, perhaps you want this ?
args.Count = (args.Count == null) ? objects.Count() : args.Count + objects.Count();
Related
I want to create global class(So I have to write it only once, not in all forms )that should detect if the global int is certain number and then overwrite text in label that says what is wrong.
For example when your name is too short it will says that your name is too short.
I know that you can do this in form, but I want whole new separated class because I plan to do this for 3 forms, so I would have to copy and paste the same errors etc... in each of them.
example:
Code in form register_menu.cs
public void getErrorNumber()
{
if (char_amount_username < 3 || textBox_username.Text == "Username")
{
Variables.error_number = 1;
}
else if (email_adress.Contains("#") != true || textBox_emailadress.Text == "Email adress")
{
Variables.error_number = 2;
}
else if (char_amount_password < 7 || textBox_password.Text == "Password")
{
Variables.error_number = 3;
}
else if (textBox_password.Text != textBox_passwordconfirm.Text)
{
Variables.error_number = 4;
}
else
{
Variables.error_number = 0;
}
}
private void button1_Click_1(object sender, EventArgs e)
{
ErrorCheck();
}
Code in class named GlobalErrorChecker.cs
namespace Xenious
{
internal class ErrorVariable
{
public static int error_number;
public void ErrorCheck()
getErrorNumber();
{
if (ErrorVariable.error_number is 1) ;
{
register_menu.error_msg.Text = "Your username is invalid!\n ⬤ Username must be avaiable\n ⬤ Minimum lenght of username is 4 ";
register_menu.displayERROR(true);
}
if(ErrorVariable.error_number is 2);
{
register_menu.error_message.Visible = true;
register_menu.error_msg.Text = "Your password is invalid!\n ⬤ Minimum lenght of password is 8 ";
register_menu.ICON_password_error.Visible = true;
}
if (ErrorVariable.error_number is 6) ;
{
login_menu.error_message.Visible = true;
login_menu.error_msg.Text = "Your username and password do not match!";
login_menu.ICON_username_error.Visible = true;
login_menu.ICON_password_error.Visible = true;
}
}
}
}
This is just example code how I want to do this, I have multiple problems.
First problem is that class GlobalErrorChecker.cs doesnt detect any controls from forms at all. I tried to fix this by looking online but I could find anything.
Second problem is that it says register_menu.ICON_password_error is unavaiable due to protection error CS0122....
I tried quite a huge amount of different methods how to do this, but I only found how to do this between 2 diffent forms, not between form and new class
Consider using a validation library like FluentValidation.
First create a class with properties to validate e.g.
public class Person
{
public string UserName { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string PasswordConfirmation { get; set; }
}
Create a validator.
public class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(person => person.UserName)
.NotEmpty()
.MinimumLength(3);
RuleFor(person => person.EmailAddress).EmailAddress();
RuleFor(person => person.Password.Length).GreaterThan( 7);
RuleFor(person => person.Password).Equal(p => p.PasswordConfirmation);
}
}
Create a new instance of the class, in this case Person. Populate values from your controls and than perform validation via Validate off the PersonValidator.
Below is a mock-up which uses StringBuilder to create a string which can be presented to the user.
Person person = new Person()
{
UserName = "billyBob",
Password = "my#Password1",
EmailAddress = "billyBob#gmailcom",
PasswordConfirmation = "my#Password1"
};
PersonValidator validator = new PersonValidator();
ValidationResult result = validator.Validate(person);
if (result.IsValid)
{
// person is valid
}
else
{
StringBuilder builder = new StringBuilder();
foreach (var error in result.Errors)
{
builder.AppendLine(error.ErrorMessage);
}
}
Dig in to ValidationResult if you need to inspect individual errors like PropertyName (Name of the property being validated) and ComparisonValue (Value that the property should not equal).
Note The email address validator only checks if an email address has a # so if you need more custom work is needed.
Making a static Extension Method
is one way to "create global class (so I have to write it only once...)". This answer will explore this option step-by-step.
First ask "what info is needed to determine a valid form?" Define these requirements in an interface:
interface IValidate
{
// Inputs needed
public string Username { get; }
public string Email { get; }
public string Password { get; }
public string Confirm { get; }
}
Now declare an Extension Method for "any" form that implements IValidate (this is the global class you asked about).
static class Extensions
{
public static int ValidateForm(this IValidate #this, CancelEventArgs e)
{
if (#this.Username.Length < 3) return 1;
if (!#this.Email.Contains("#")) return 2;
if (#this.Password.Length < 7) return 3;
if (!#this.Password.Equals(#this.Confirm)) return 4;
return 0;
}
}
The Form classes will be able to call the extension using this.
var e = new new CancelEventArgs();
int error_number = this.ValidateForm(e);
Example of Form Validation
For each of your 3 forms, implement the interface. This just means that the 3 form classes are making a promise or contract to provide the information that the interface requires (in this case by retrieving the text from the textboxes).
public partial class MainForm : Form, IValidate
{
#region I M P L E M E N T I N T E R F A C E
public string Username => textBoxUsername.Text;
public string Email => textBoxEmail.Text;
public string Password => textBoxPassword.Text;
public string Confirm => textBoxConfirm.Text;
#endregion I M P L E M E N T I N T E R F A C E
.
.
.
}
Then call the extension method when any textbox loses focus or receives an Enter key.
public partial class MainForm : Form, IValidate
{
public MainForm()
{
InitializeComponent();
foreach (Control control in Controls)
{
if(control is TextBox textBox)
{
textBox.TabStop = false;
textBox.KeyDown += onAnyTextboxKeyDown;
textBox.Validating += onAnyTextBoxValidating;
textBox.TextChanged += (sender, e) =>
{
if (sender is TextBox textbox) textbox.Modified = true;
};
}
}
}
private void onAnyTextBoxValidating(object? sender, CancelEventArgs e)
{
if (sender is TextBox textBox)
{
// Call the extension method to validate.
ErrorInt #int = (ErrorInt)this.ValidateForm(e);
if (#int.Equals(ErrorInt.None))
{
labelError.Visible = false;
buttonLogin.Enabled = true;
return;
}
else if (textBox.Modified)
{
buttonLogin.Enabled = false;
BeginInvoke(() =>
{
switch (#int)
{
case ErrorInt.Username: textBoxUsername.Focus(); break;
case ErrorInt.Email: textBoxEmail.Focus(); break;
case ErrorInt.Password: textBoxPassword.Focus(); break;
case ErrorInt.Confirm: textBoxConfirm.Focus(); break;
}
labelError.Visible = true;
labelError.Text = typeof(ErrorInt)
.GetMember(#int.ToString())
.First()?
.GetCustomAttribute<DescriptionAttribute>()
.Description;
textBox.Modified = false;
textBox.SelectAll();
});
}
}
}
private void onAnyTextboxKeyDown(object? sender, KeyEventArgs e)
{
if (sender is TextBox textbox)
{
if (e.KeyData.Equals(Keys.Return))
{
// Handle the Enter key.
e.SuppressKeyPress = e.Handled = true;
MethodInfo? validate = typeof(TextBox).GetMethod("OnValidating", BindingFlags.Instance | BindingFlags.NonPublic);
CancelEventArgs eCancel = new CancelEventArgs();
validate?.Invoke(textbox, new[] { eCancel });
}
}
}
.
.
.
}
Where:
enum ErrorInt
{
None = 0,
[Description("Username must be at least 3 characters.")]
Username = 1,
[Description("Valid email is required.")]
Email = 2,
[Description("Password must be at least 7 characters.")]
Password = 3,
[Description("Passwords must match.")]
Confirm = 4,
}
I have a combobox in my application, where items are loaded asynchronously depending on a search text you can enter in the text field.
This works fine, but every time the text of the first item is automatically selected during updating the datasource of the combobox.
This leads to unintended behaviour, because I need to have the search text entered by the user to stay in the textfield of the combobox until a selection is done by the user and not automatically overwrite the text with the first entry.
This is my code:
public partial class ProductGroupDescription : UserControl, Interfaces.TabPages.ITabPageProductGroupDescription
{
private Services.IProductGroupDescriptionService _ApplicationService;
public BindingList<ProductGroup> ProductGroups { get; set; } = new BindingList<ProductGroup>();
public string ProductGroupSearchText { get; set; } = string.Empty;
public ProductGroupDescription(Services.IProductGroupDescriptionService applicationService)
{
InitializeComponent();
InitialSetupControls();
_ApplicationService = applicationService;
}
public void InitialSetupControls()
{
var pgBindingSource = new BindingSource();
pgBindingSource.DataSource = ProductGroups;
Cbo_ProductGroup.DataSource = pgBindingSource.DataSource;
Cbo_ProductGroup.DataBindings.Add("Text", ProductGroupSearchText, "");
}
private async void Cbo_ProductGroup_TextChanged(object sender, EventArgs e)
{
if (Cbo_ProductGroup.Text.Length >= 2)
{
ProductGroupSearchText = Cbo_ProductGroup.Text;
Cbo_ProductGroup.SelectedIndex = -1;
bool withStopFlagged = Chk_StopFlag_PGs_Included.Checked;
List<ProductGroup> list = await _ApplicationService.GetProductGroupBySearchString(ProductGroupSearchText, withStopFlagged);
if (list != null && list.Count > 0)
{
ProductGroups.Clear();
list.ForEach(item => ProductGroups.Add(item));
Cbo_ProductGroup.DroppedDown = Cbo_ProductGroup.Items.Count > 0 && Cbo_ProductGroup.Focused;
}
}
}
}
I tried to set Cbo_ProductGroup.SelectedIndex = -1, but it does not solve my issue here.
I also saw this on SO: Prevent AutoSelect behavior of a System.Window.Forms.ComboBox (C#)
But is there really no simpler solution to this issue?
I got it to work now.
It worked, when I removed the binding of the text field of the combobox
Cbo_ProductGroup.DataBindings.Add("Text", ProductGroupSearchText, "");
and set the new (old) value directly to the text field of the combobox.
Cbo_ProductGroup.Text = searchText;
In this case, a new event Text_Changed is fired and so the application has a infinite loop. So I used a property (ShouldTextChangedEventBeIgnored), if the Text_Changed event should be ignored.
Thanks to #CaiusJard for many hints in the comments.
This is my final code:
public partial class ProductGroupDescription : UserControl, Interfaces.TabPages.ITabPageProductGroupDescription
{
private ApplicationLogic.Interfaces.Services.IProductGroupDescriptionService _ApplicationService;
public BindingList<ProductGroup> ProductGroups { get; set; } = new BindingList<ProductGroup>();
public bool ShouldTextChangedEventBeIgnored { get; set; } = false;
public ProductGroupDescription(ApplicationLogic.Interfaces.Services.IProductGroupDescriptionService applicationService)
{
_ApplicationService = applicationService;
InitializeComponent();
InitialSetupControls();
}
public void InitialSetupControls()
{
var pgBindingSource = new BindingSource();
pgBindingSource.DataSource = ProductGroups;
Cbo_ProductGroup.DataSource = pgBindingSource.DataSource;
}
private async Task<List<ProductGroup>> LoadProductGroupItems(string searchText)
{
bool withStopFlagged = Chk_StopFlag_PGs_Included.Checked;
return await _ApplicationService.GetProductGroupBySearchString(searchText, withStopFlagged);
}
private async Task SetProductGroupSearchBoxItems(List<ProductGroup> list, string searchText)
{
await Task.Run(() =>
{
if (list != null && list.Count > 0)
{
ShouldTextChangedEventBeIgnored = true;
Cbo_ProductGroup.Invoke((c) =>
{
ProductGroups.Clear();
list.ForEach(item => ProductGroups.Add(item));
c.DroppedDown = c.Items.Count > 0 && c.Focused;
c.Text = searchText;
c.Select(c.Text.Length, 0);
});
ShouldTextChangedEventBeIgnored = false;
}
});
}
private async void Cbo_ProductGroup_TextChanged(object sender, EventArgs e)
{
try
{
if (Cbo_ProductGroup.Text.Length >= 2 && ShouldTextChangedEventBeIgnored == false)
{
string searchText = Cbo_ProductGroup.Text;
List<ProductGroup> list = await LoadProductGroupItems(Cbo_ProductGroup.Text);
await SetProductGroupSearchBoxItems(list, searchText);
}
}
catch(Exception ex)
{
System.Diagnostics.Trace.Write(ex);
}
}
}
this below to sample code;
private ExampleStatus _status;
public ExampleStatus status
{
get
{
if (_status == null) _status = new ExampleStatus();
//if (_status.receivedData) _status.receivedData = false; //this line is incorrect !
return _status;
}
}
public class ExampleStatus
{
public int Id { get; set; }
public string Name { get; set; }
public bool receivedData { get; set; }
//I don't want to use this method
public void Clear()
{
Id = 0;
Name = string.Empty;
receivedData = false;
}
}
int stateType = 0;
void ContinuousLoop(ExampleStatus statusObj)
{
while (true)
{
//I don't want to use the options below
//statusObj.Clear();
//or
//statusObj = new ExampleStatus();
if (stateType == 0)
{
statusObj.Id = 0;
statusObj.Name = "Firs Status";
}
else if (stateType == 1)
{
statusObj.Id = 1;
statusObj.Name = "Second Status";
statusObj.receivedData = true;
}
else if (stateType == 2)
{
statusObj.Id = 2;
statusObj.Name = "Third Status";
}
}
}
void RunThread()
{
var t1 = new Thread(() =>
{
ContinuousLoop(status);
});
t1.Start();
}
Is it possible to set default values without a method or new instance, as shown in the example?
Actually that's why I'm asking this question:
I will use the class I have defined in many places. I will need to add a block of code, such as the Clear method, to every place I use it.
I'm also curious about one more thing. If I assign a new instance every time to reset my objects, does this cause problems in memory?
I know more or less how garbage collections work. However, they say that in practice it does not work as said in theory.
So if I add "IDisposable" to my Class, it would tell the garbage collector: Welcome, I'm a litter. Will you take me too?
So I'm trying to implement unit testing into my application and I am trying to mock a number of my constructor arguments to pass through. I've read on here that you are not supposed to Mock a class and instead Mock an interface but I've also read that there isn't much point in implementing an interface for a presenter when implementing the MVP approach.
I've been told that you can pass in the parameters into a constructor by using this line, this is also the line where the error is thrown:
var p = new ImpositionCalculatorPresenter(mockView.Object, mockImpositionCalculatorManager.Object, mockSystemVariablesManager.Object,
mockSystemVariablesPresenter.Object, mockPrintingDesignManager.Object);
Can anyone help me implement a way of passing in the arguments into my constructor when Mocking my presenter?
[TestMethod]
public void CalculateFinalImpositionPlates() {
//Need to pass in the managers for dependency injection.
//Then I need to find a way which will trigger the event to call the method on the presenter
//Then do my tests
var mockView = new Mock<IImpositionFormView>();
mockView.SetupProperty(r => r.FinalImpositionOut, 0);
//These may have to be manually mocked by me so they don't affect JSON file
var mockImpositionCalculatorManager = new Mock<IImpositionCalculatorManager>();
var mockSystemVariablesManager = new Mock<ISystemVariablesManager>();
var mockSystemVariablesPresenter = new Mock<SystemVariablesPresenter>();
var mockPrintingDesignManager = new Mock<IPrintingDesignManager>();
//Fix error allowing me to pass in objects to the contstructor
var p = new ImpositionCalculatorPresenter(mockView.Object, mockImpositionCalculatorManager.Object, mockSystemVariablesManager.Object,
mockSystemVariablesPresenter.Object, mockPrintingDesignManager.Object);
mockView.SetupProperty(r => r.Presenter == p);
mockView.Raise(r => r.CalculateFinalImpositionPlates += null);
Assert.Equals(mockView.Object.Plates, 0);
}
Part of my presenter:
public class ImpositionCalculatorPresenter {
private readonly IImpositionCalculatorManager _impostionFormManager;
private readonly ISystemVariablesManager _systemVariablesManager;
private readonly SystemVariablesPresenter _systemVariablesFormPresenter;
private readonly IPrintingDesignManager _printingDesignManager;
private readonly string _pathToAppSettings = $"{AppDomain.CurrentDomain.BaseDirectory}/PrintAppSettings.txt";
private PagePrintingDesignParameters _printingAppParameters;
private readonly IImpositionFormView _view;
public ImpositionCalculatorPresenter(IImpositionFormView View, IImpositionCalculatorManager impositionFormManager,
ISystemVariablesManager systemVariablesManager, SystemVariablesPresenter systemVariablesFormPresenter, IPrintingDesignManager printingDesignManager) {
_view = View;
_impostionFormManager = impositionFormManager;
_systemVariablesManager = systemVariablesManager;
_systemVariablesFormPresenter = systemVariablesFormPresenter;
_printingDesignManager = printingDesignManager;
InitialiseEvents();
}
private void InitialiseEvents() {
_view.CalculateFinalImpositionPlates += CalculateFinalImpositionPlates;
}
private void CalculateFinalImpositionPlates(object sender, EventArgs e) {
//if sheet fed checked, and sheetwise not selected in drop down and sheetsize irregular not select in drop down
try {
if (_view.FinalImpositionOut != 0) {
if (_view.OverridePlates != 0) {
_view.Plates = _view.OverridePlates;
}
else if (_view.PrintingStyleChecked == PrintingStyle.SheetFed && (_view.CboImposition != "Sheetwise" || _view.CboImposition != "Irregular S\\Wise")) {
_view.Plates = _view.ColourSideOne;
}
else if (_view.ColourSideOne != 0 && _view.ColourSideTwo != 0) {
_view.Plates = _view.ColourSideOne + _view.ColourSideTwo;
}
}
}
catch (Exception ex) {
LogErrorToView(this, new ErrorEventArgs(ex.Message));
}
}
}
I wrote a class ReasXML the basically read only XML files. You see I worked withe events.. Now the problem is I want to use the result of lsTags or I want the values of my XML file. The basic thought to do that: make the function a return type List for the method LoadXMLFile and XMLFileLoaded. But I receive an error that, has the wrong return type.
Can someone help me with this of give me an example with events how to return variables?
Thanks in advance!
public void LoadXMLFile() {
WebClient xmlClient = new WebClient();
xmlClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(XMLFileLoaded);
xmlClient.DownloadStringAsync(new Uri("codeFragments.xml", UriKind.RelativeOrAbsolute));
}
protected void XMLFileLoaded(object sender, DownloadStringCompletedEventArgs e) {
if (e.Error == null) {
string xmlData = e.Result;
XDocument xDoc = XDocument.Parse(xmlData);
var tagsXml = from c in xDoc.Descendants("Tag") select c.Attribute("name");
List<Tag> lsTags = new List<Tag>();
foreach (string tagName in tagsXml) {
Tag oTag = new Tag();
oTag.name = tagName;
var tags = from d in xDoc.Descendants("Tag")
where d.Attribute("name").Value == tagName
select d.Elements("oFragments");
var tagXml = tags.ToArray()[0];
foreach (var tag in tagXml) {
CodeFragments oFragments = new CodeFragments();
oFragments.tagURL = tag.Attribute("tagURL").Value;
oTag.lsTags.Add(oFragments);
}
lsTags.Add(oTag);
}
//List<string> test = new List<string> { "a","b","c" };
lsBox.ItemsSource = lsTags;
}
}
Event handlers can be any delegate type, not just EventHandler.
If you want to return a result, simply change the event to use
Func<CustomEventArgs, YourReturnType>
Here is some sample code:
using System;
class Program
{
public class Ev
{
public int? RaiseSomeEvent()
{
if (SomeEvent != null)
{
return SomeEvent();
}
return null;
}
public event Func<int> SomeEvent;
}
static void Main(string[] args)
{
var ev = new Ev();
ev.SomeEvent += ev_someEvent1;
ev.SomeEvent += ev_someEvent2;
int? value = ev.RaiseSomeEvent();
Console.WriteLine(value.HasValue ? value.Value.ToString() : "(null)");
}
static int ev_someEvent1()
{
return 5;
}
static int ev_someEvent2()
{
return 6;
}
}
The output of this code:
6
Multiple event handlers
Notice that you only get the last value returned by all of the event handlers.
If you want to handle multiple return values in a somewhat event-like fashion, you may want to check out the Visitor Design Pattern instead. If you use this pattern, you would have to create a visitor adapter (or visitor adapters) that has the Accept methods on it.