trigger method in inherited class - windows forms - winforms

this is my scenario, and I want to know if it's possible to accomplish what I intend to do:
I have a class library (made in c#) that inside has a class (named SForm) that inherits from System.Windows.Forms.Form. In that class I declare some strings, and methods to set those strings' values.
public class SForm : Form
{
public string _myDate;
public void setTime(string val) { _mydate = val; }
}
In another class, I make some calls to an API that trigger callbacks, and when a callback occurs, call the methods to set the values in the class that inherits from Form. All working fine, and all classes are packed in a DLL.
public class Events
{
private SForm _form;
public void setForm(SForm f)
{
_form = f;
}
public void connect()
{
//when I call this method, connects to a device using the API, and if
//it's succesful, triggers OnCallback...
}
private void OnCallback(string retVal)
{
_form.setTime(retVal); //this works
}
}
Here is my problem: I have a desktop app, in VB, that uses that DLL, and when I inherit from SForm, I want that the callback triggered by the DLL invokes the method in my form
Public Class Form1
Inherits SForm
Private _se As Events
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
_se = New Events()
_se.setForm(Me)
_se.connect()
End Sub
Private Overloads Sub setTime(ByVal s As String)
MessageBox.Show(s)
End Sub
What I need is that the callback trigger the "setTime" method in this form, and show the value sent by my DLL (I can push a button and access the value of the _myDate string, but I need it to be automatic). Is that possible? How??
Thanks a million

You need to declare setTime() virtual so you can override it. Don't forget to call the base method.

Related

How To MustOverride Shared/Constructor Function Visual Basic

I'm currently using Visual Basic for a College Project which requires us to make a simple database system. For my system I have a base(abstract) class called Record which is inherited by the different types of records there are in my database e.g. Member, User, Role.
I am saving my data in csv files and have already written a CSVHandler class. However, I want an elegant way of constructing an instance of a class derived from Record with a string from the CSVHandler.
This is where the problem occurs. The only way I can think of doing this is by making a Constrcutor or Shared Function in each class derived from Record. However, Visual Basic does not allow you make Constructors or Shared Functions also MustOverride.
Here is the code I would expect to write:
' Base Class
Public MustInherit Class Record
Public MustOverride Shared Function fromString(ByVal str as String) As Record
End Class
' Example Of Class Derived From Record
Public Class User
Inherits Record
Private _id As String
Private _name As String
Public Sub New(ByVal id As String, ByVal name As String)
_id = id
_name = name
End Sub
Public Overrides Shared Function fromString(ByVal str as String) As Record
Dim strs() As String = str.Split(",")
Return New User(strs(0), strs(1))
End Function
End Class
' Example Of Creating Instacnce Of User
Dim user1 = User.fromString("1671,Kappeh")
Is there a way to achieve this effect?
Have your constructor call a Protected MustOverride method that does the initialisation.
Public MustInherit Class Record
'This is required because each derived constructor must be able to implicitly invoke a parameterless
'base constructor if it doesn't explicitly invoke a base constructor with parameters.
Protected Sub New()
End Sub
Public Sub New(csv As String)
Init(csv)
End Sub
Protected MustOverride Sub Init(csv As String)
End Class
Public Class User
Inherits Record
Private Property Id As String
Private Property Name As String
'This is still required because you can use a base constructor directly to create a derived instance.
Public Sub New(csv As String)
MyBase.New(csv)
End Sub
Public Sub New(id As String, name As String)
Id = id
Name = name
End Sub
Protected Overrides Sub Init(csv As String)
'Add your type-specific implementation here.
End Sub
End Class
This "solution" doesn't actually do what I thought it would because, while it forces you to override Init in a derived class, you still have to provide a derived constructor that invokes the base constructor that calls Init and you still can't enforce that. I think that I'll leave this as an answer though, because, while it doesn't actually provide a solution to your problem, it demonstrates further why (as far as I can tell) there is no such solution.
The following is similar to the answer from #jmcilhinney in that it forces the derived class to implement an initialization method. However it makes use of a generic shared function and uses the little known GetUninitializedObject method to get around using the generic New constraint and it's requirement of an accessible parameter-less constructor.
Public MustInherit Class Record
Public Shared Function fromString(Of T As {Record})(ByVal str As String) As T
' create an unintialized instance of T
Dim ret As T = DirectCast(System.Runtime.Serialization.FormatterServices.GetUninitializedObject(GetType(T)), T)
ret.Initialize(str)
Return ret
End Function
Protected MustOverride Sub Initialize(source As String)
End Class
The User class then would be something like this:
Public Class User : Inherits Record
Private _id As String
Private _name As String
Public Sub New(ByVal id As String, ByVal name As String)
_id = id
_name = name
End Sub
Protected Overrides Sub Initialize(source As String)
Dim strs() As String = source.Split(","c)
_id = strs(0)
_name = strs(1)
End Sub
End Class
Example usage:
Dim userRecord As User = Record.fromString(Of User)("1,2")

Make WPF-control shared

I'm trying to access Label from another class's method running in background thread with the help of MainWindow Class's Public Shared Sub like this:
Private Delegate Sub ProgressReportInvoker(ByVal progressStr As String)
Public Shared Sub ProgressReport(ByVal progressStr As String)
If MainWindow.Label.Dispatcher.CheckAccess() Then
MainWindow.Label.Content = progressStr
Else
MainWindow.Label.Dispatcher.Invoke(
New ProgressReportInvoker(AddressOf ProgressReport),
progressStr)
End If
End Sub
Call from another class is below:
MainWindow.ProgressReport("Sample text")
But I have this error on "MainWindow.Label":
Reference to a non-shared member requires an object reference.
I noticed that if I declare Label in MainWindow.g.i.vb as Public Shared than error is gone:
#ExternalSource ("..\..\MainWindow.xaml", 11)
<System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")>
Public Shared WithEvents Label As System.Windows.Controls.Label
#End ExternalSource
But this file is generated automatically from the *.XAML file so it takes previous look when I compile the code.
Is there any way to make control shared in *.XAML file or may be there are any alternatives of making my task possible?
You should access the instance of the MainWindow and not the type itself:
Public Shared Sub ProgressReport(ByVal progressStr As String)
Dim mainWindow = Application.Current.Windows.OfType(Of MainWindow).FirstOrDefault()
If mainWindow.Label.Dispatcher.CheckAccess() Then
mainWindow.Label.Content = progressStr
Else
mainWindow.Label.Dispatcher.Invoke(
New ProgressReportInvoker(AddressOf ProgressReport),
progressStr)
End If
End Sub
I tried this before but problem is in multitasking. I can't access the form from another thread without some special moves which I don't know about
You can only access a UI control in the thread on which it was originally created:
Application.Current.Dispatcher.BeginInvoke(New Action(Sub()
Dim mainWindow = Application.Current.Windows.OfType(Of MainWindow).FirstOrDefault()
mainWindow.Label.Content = progressStr
End Sub))
It is very bad practice to use anything global (shared/static). Use instance of class or other mechanism (Dependency Injection, messaging, events, etc.) for communication between independent classes.

Ninject ActivationException in WinForms application

I am working on a project to implement DI (using Ninject) into an existing WinForms application, however I have hit a few issues and my knowledge of working with DI in WinForms is limited at best, however I have successfully used it several times in MVC projects.
I get this message when trying to create a form that has a constructor that requires an interface of a repository:
Error activating IProductionRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency IProductionRepository into parameter
productionRepository of constructor of type Main
1) Request for Main
I have searched on the web, but most of the articles I have read on this error either a dealing with much more complicated setups or are regarding parameter injection, which I am not sure is the issue here.
I have a form that is to launch the form that is to use DI (the error occurs on the kernel.Get call:
Private Sub txt_Click(sender As System.Object, e As System.EventArgs) Handles txt.Click
Try
Dim kernel As Ninject.IKernel =
New Ninject.StandardKernel(New NinjectFactory())
Dim form As Main = kernel.Get(Of Main)()
form.ConnectionString = App.pstrConnectString
form.ShowDialog(Me)
Catch ex As Exception
Support.ErrorHandler.ReportError(ex.Message, ex, True)
End Try
End Sub
I have my NinjectFactory with the correct binding (and several other commented out attempts):
public class NinjectFactory : NinjectModule
{
private IKernel _ninjectKernel;
public NinjectFactory()
{
_ninjectKernel = new StandardKernel();
}
public override void Load()
{
//_ninjectKernel.Bind(typeof(IRepository<>))
// .To(typeof(GenericRepository<>));
//_ninjectKernel.Bind(typeof(IProductionRepository))
// .To(typeof(ProductionRepository));
_ninjectKernel.Bind<IProductionRepository>().To<ProductionRepository>();
}
}
Finally I have the form I am trying to launch:
private IProductionRepository _productionRepository;
public string ConnectionString
{
get
{
return _connectionString;
}
set
{
_connectionString = value;
}
[Inject]
public Main(IProductionRepository productionRepository) : this()
{
this._productionRepository = productionRepository;
}
public Main()
{
InitializeComponent();
}
This is how I have used Ninject in MVC projects before and I haven't had an issue, however obviously there is something different regarding WinForms.
Any help would be greatly appreciated.
I suggest to use single point of dependency resolution, know as Composition Root, suggested by Mark Seemann (#mark-seemann) in his great book Dependency Injection in .NET. Your CompositionRoot might look like this:
public class CompositionRoot
{
private static IKernel _ninjectKernel;
public static void Wire(INinjectModule module)
{
_ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return _ninjectKernel.Get<T>();
}
}
public class ApplicationModule : NinjectModule
{
public override void Load()
{
Bind(typeof(IRepository<>)).To(typeof(GenericRepository<>));
}
}
You can wire them in Program
CompositionRoot.Wire(new ApplicationModule());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CompositionRoot.Resolve<Form1>());
Now your button handler could look like this:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(_productionRepository.ToString());
}
Note: if you want to test your application with different dependencies, which is probably the case, then you create another module with different wiring configuration. In tests you will have another wiring logic with stubs and mocks.
I also don't like NInject attributes on my model and if you use constructor injection - you can get rid of them. My entities are simple POCOs.
public interface IProductionRepository
{
}
public class ProductionRepository : IProductionRepository
{
public override string ToString()
{
return "Foo";
}
}
Mark also makes a great case for WinForms with DI pattern and how it can be implemented, i really suggest his book.

MVP Pattern What to include on the View Interface

Using a MVP Pattern in a WinForms app I have been asked to write. Pardon VB.net as I am being forced to use this :(
Being New to MVP I have gone with a Passive Model implementation where there is no dependency between the View & the Model and only the Presenter knows both
The View being a representation of the UI what functionality should be part of the IVIEW interface
Should I have the methods/actions/tasks in the IView i.e
Property QItems As IList(Of QItem)
Property SelectedQItem As QItem
Property QueStatus As QueStatus
Property ReportName As String
Property ScheduleName As String
Sub BuildQItems()
Sub RunQue()
Sub StopQue()
Sub CancelCurrent()
Sub PauseCurrent()
and make the calls view the Iview Interface that is implemented in the winform
class Winform
implements IView
Private Sub btnCreate_Click(sender As System.Object, e As System.EventArgs) Handles btnCreate.Click Implements IVIEW.Create
If (_presenter.CreateSchdule()) Then
MessageBox.Show("Sucessfully Created")
Close()
End If
End Sub
End Class
or Should I just hold the state
Property QItems As IList(Of QItem)
Property SelectedQItem As QItem
Property QueStatus As QueStatus
Property ReportName As String
Property ScheduleName As String
And make the calls directly to the Presenter which is part of the WinForm and not bother about the Iview intreface
i.e
_presenter.BuildItems()
_presenter.RunQue()
How do you weigh up when to do either approach when using MVP ?
If you are referring to the passive view approach then you should not try to call the presenter or to write business logic inside the view. Instead, the view should create an instance of the presenter passing a reference of itself. Login form example:
public LoginView() // the Form constructor
{
m_loginPresenter = new LoginPresenter(this);
}
public void ShowLoginFailedMessage(string message)
{
lblLoginResult.Text = message;
}
The View interface should contain properties that allow the presenter to present business objects to the view as well as to manage the UI state (indirectly). Ex:
interface ILoginView
{
event Action AuthenticateUser;
string Username { get; }
string Password { get; }
string LoginResultMessage { set; }
}
The presenter would be something like:
public LoginPresenter(ILoginView view)
{
m_view = view;
m_view.AuthenticateUser += new Action(AuthenticateUser);
}
private void AuthenticateUser()
{
string username = m_view.Username;
...
m_view.ShowLoginResultMessage = "Login failed...";
}
Sorry about the C# code but I haven't touched VB.NET for a while now.

vb.net - accessing an initiated class from another class

I have the following defined in my "Form1":
Dim Database As New DatabaseManager
So that I can access all my database functions anywhere in the main form (database connection is initialized via the "private sub new()" function in the DatabaseManager class.
The works fine for all queries done in form1, however I have many other classes I'd like to be able to access the database from.
Does anyone know how to access the initiated class in my example? Since right now it is initiated in the main form, if I try to access any functions inside any other class functions it does nothing (doesn't error out either).
I'm trying to figoure out how to dim a class one and I can access it from within any class and I can't figure it out.
Thanks!
I would encourage to use dependency injection if you want this.
In essence it would come down to this.
Private _DatabaseManager as DatabaseManager
Public Sub New(Byval DatabaseManager as DatabaseManager)
InitializeComponent()
_DatabaseManager = DatabaseManager
End Sub
TIf you do it like this you can give all your forms the same DatabaseManager or a different one like you please.
There is of course a lot more to it than that . But for that you will have to dig into Dependecy Injection and Inversion of control (DI/IoC)
One thing you could do is create a factory for the DatabaseManager and then just have all your other code call it from that factory. I'm very out of practice with VB syntax, but in C# it might look something like this:
public class DatabaseManagerFactory
{
private static DatabaseManager _current = null;
public static DatabaseManager Current
{
get
{
if (_current == null) _current = new DatabaseManager();
return _current;
}
}
}
VB
Public Class DatabaseManagerFactory
Private Shared _current As DatabaseManager = Nothing
Public Shared ReadOnly Property Current As DatabaseManager
Get
If _current Is Nothing null Then _current = New DatabaseManager()
Return _current
End Get
End Property
End Class
The idea then is that anything in your application which needs to use a DatabaseManager would just call DatabaseManagerFactory.Current to get the one shared instance.
Note that in this case DatabaseManager isn't really a singleton, you can still instantiate one elsewhere in the application if you need to for some reason. If it should be an actual singleton then you'd want to make some modifications to the DatabaseManager class itself. Maybe give it a private constructor and implement this factory directly on the class? Something like this:
public class DatabaseManager
{
private static DatabaseManager _current = null;
public static DatabaseManager Current
{
get
{
if (_current == null) _current = new DatabaseManager();
return _current;
}
}
private DatabaseManager
{
// your initialization of the class
}
}
VB
Public Class DatabaseManager
Private Shared _current DatabaseManager = Nothing
Public Shared ReadOnly Property Current As DatabaseManager
Get
If _current Is Nothing Then _current = New DatabaseManager()
Return _current
End Get
End Property
Private Sub New()
' your initialization of the class
End Sub
End Class
(I encourage anybody more familiar with VB syntax to edit this answer accordingly to better address the question.)
create and initiate databaseManager in your Form1, but declare it Friend instead of Dim. That way you can write a reference to it like : Form1.databaseManager

Resources