I have a class called AppViewModel, this class it's responsible to control the screens. AppViewModel extends my BaseConductor:
public class BaseConductor : Conductor<Screen>.Collection.OneActive
{
...
}
Then, I call a viewmodel (UserControl) on the constructor of AppViewModel:
this.ActivateItem(new FirstViewModel());
On FirstViewModel, after the user clicks on a button I want to open SecondViewModel and close the FirstViewModel:
var conductor = this.Parent as IConductor;
conductor.DeactivateItem(this, true);
conductor.ActivateItem(new SecondViewModel(param));
I already tried to do this:
((IApp)this.Parent).ActivateItem(new SecondViewModel(param));
TryClose();
SecondViewModel extends my BaseScreen:
public class BaseSceen : Screen
{
...
}
I want to close the FirstViewModel, because on the FirstViewModel and SecondViewModel I have shortcuts. When I'm with the SecondViewModel opened I hit a shortcut, and the method that is executed it's from FirstViewModel. So, the FirstViewModel still running.
How can I close the FirstViewModel, and avoid this problem with shortcuts?
Thanks!
Do you really need to use Conductor<T>.Collection.OneActive? You can just use Conductor<T> so that activating an item will automatically deactivate and close the previously active item. And also, is it required that the button/action pair reside in the FirstViewModel? I suggest that you just put those in the AppViewModel and let it orchestrate the navigation and activation/deactivation of the two child screens.
public AppViewModel : Conductor<Screen>
{
public void AppViewModel()
{
ActivateItem(new FirstViewModel());
}
public void ActivateSecondViewModel()
{
// FirstViewModel will automatically be deactivated
// and closed since we are using plain Conductor<T>
ActivateItem(new SecondViewModel());
}
}
I found it! The shortcut event was attached to the window, not to the usercontrol.
So, event when usercontrol was ended the event still "attached" to the window. Now, I added an method that is called when UserControl is Unloaded, to "deattach" the event.
Bad mistake!
Related
I have a main form that acts as a "Wizard" for a bunch of different user controls. I have one user control with relatively basic operations, and I am trying to create a new user control that inherits this basic user control.
However, the base user control has a variable containing the main form (so the user control can access the wizard control functions in the main form). When I create a new "inherited user control" the Designer complains that the reference to the main for has not been set to an instance of the object. I set the reference to the MainForm during runtime when I create an instance of the base user control.
Is there a way to make this work? Below is some simplified code demonstrating my problem.
MainForm.cs:
public partial class MainForm : Form
{
public string exampleString = "From MainForm";
public MainForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
BaseControl base = new BaseControl();
base.mainForm = this;
{
}
BaseControl.cs
public partial class BaseControl : UserControl
{
public MainForm mainForm { get; set;}
public TestPanel()
{
InitializeComponent();
string needed = mainForm.exampleString; //Object reference not set to an instance of an object here
}
}
So when I try to create a user control that inherits BaseControl through Solution -> Add -> New Item -> Inherited User Control and select BaseClass, the designer complains of the "Object reference not set to an instance of an object" error at the string needed = mainForm.exampleString line in BaseControl.cs.
Any help with this would be greatly appreciated. Hopefully this is enough information for you to understand what I am trying to do.
Thanks.
The code which you shared will not work, neither at run-time nor in design-time.
You are trying to use mainForm.exampleString in constructor of BaseControl while mainForm will be assigned just after creating an instance of BaseControl.
In such cases, specially when you want to have design-time support, you can derive from ISupportInitialize interface and override BeginInit and EndInit.
Example
The following control implements ISupportInitialize. If you drop an instance of the control on the form, at run-time, it tries to find the parent form and if it was MainForm tries to use public members of the MainForm:
using System.ComponentModel;
using System.Windows.Forms;
public class MyControl : Control, ISupportInitialize
{
public void BeginInit()
{
}
public void EndInit()
{
var parent = this.FindForm() as MainForm;
if (parent != null)
{
//Access to MainForm members
}
}
}
This is just an example that shows how to use ISupportInitialize. In action, it's not a good idea to have a dependency to a specific type of parent form. A better idea as already mentioned in Jimi's comment is relying on interfaces. For example you can have a property of ISomeInterface in your control. The interface should contain the methods or properties which you want to have for the parent of your control. Then implement the interface in some forms. Then after you dropped an instance of your control at run-time or design-time, assign the form to the property.
I use a TabControl in one of my classes. However, I don't want to bloat this class and put all the code in it, as well as all the XAML for the TabItems.
After some Googling I've come up with the UserControl. So I created a UserControl for every TabItem, with the C# code of the TabItem in this UserControl.
The problem here, is that I need to transfer some data from my parent window (the one containing the TabControl) to the UserControls, to correctly display the data needed for that tab.
I created the UserControl in XAML and the code is like this:
<TabItem Name="userTab" Header="Gebruikers" HorizontalAlignment="Stretch">
<local:UserTabControl x:Name="userTabPanel"/>
</TabItem>
This is the constructor code for my UserControl:
public UserTabControl() {
setUsersView(); // NEED DATA FOR THIS
setUserData((User)usersView.SelectedItem);
InitializeComponent();
}
This is the class variable in the UserControl that needs a variable from the parent window: private static Parser m_config;
When I run this, I get a compile error saying :
"Object reference not set to an instance of an object."
pointing to the XAML line where I create my UserControl.
So, the conclusion here is I am yet unable to pass the variable to the UserControl and would like to have some suggestions or hints or guidance on how to do it in this case. Where am I going wrong here in achieving the required functionality ?
Im not sure that its the best way to do so, but for me it worked well.
For start I create CustomUserControl Class that inherit from UserControl with constractor that get his parent Form as an attribute like this:
public partial class CustomUserControl : UserControl
{
MainForm parentForm;
public CustomUserControl(MainForm mainForm)
{
parentForm = mainForm;
}
...
...
private void doSomthing()
{
parentForm.MainFormMember = 1;
}
}
And in the MainForm:
public partial class MainForm : Form
{
private int MainFormMember{ get; set; }
private CustomUserControl customUserControl;
public MainForm()
{
InitializeComponent();
// Create and add the CustomUserControl manually and not from ToolBox
customUserControl = new CustomUserControl(this);
this.Controls.Add(customUserControl);
}
...
...
}
In this way you can access the Form components from the UserControl
I'm currently developing a chat client/server with a WPF visual interface. One of the main function in the MainWindow class is designed to write a message in a richbox in the related WPF.
public partial class MainWindow : Window
{
// VU Window
public static MainWindow vuClient;
// VU Initialization
public MainWindow()
{
InitializeComponent();
vuClient = this;
}
static public void writeChat(User pUser, String pMessage)
{
...
// Writing in the richbox
vuClient.vuChatBox.Document.Blocks.Add(formatedMessage);
}
}
The problem is that if I call writeChat() in the MainWindow() function or through an event (onclick for example) everything works as attended, but if I try to call this function via an other class nothing happens! I don't even have an error. Do you have any clue about that issue?
No need to maintain the object "vuClient". Because writeChat is a static method.
You can directly called like below
MainWindow.writeChat();
you can't use an UI element or a function in some other class. you need to make a Delegate with an event. with the help of this event you can update your UI accordingly.
I have a very simple WinForms POC utilizing Autofac and the MVP pattern. In this POC, I am opening a child form from the parent form via Autofac's Resolve method. What I'm having issues with is how the child form stays open. In the Display() method of the child form, if I call ShowDialog() the child form remains open until I close it. If I call Show(), the child form flashes and instantly closes - which is obviously not good.
I've done numerous searches for integrating Autofac into a WinForms application, however I've not found any good examples on Autofac/WinForms integration.
My questions are:
What is the proper way to display a non-modal child form with my approach?
Is there a better way to utilize Autofac in a WinForms application than my approach?
How do I determine there are no memory leaks and that Autofac is properly cleaning up the Model/View/Presenter objects for the child form?
Only relevant code is shown below.
Thanks,
Kyle
public class MainPresenter : IMainPresenter
{
ILifetimeScope container = null;
IView view = null;
public MainPresenter(ILifetimeScope container, IMainView view)
{
this.container = container;
this.view = view;
view.AddChild += new EventHandler(view_AddChild);
}
void view_AddChild(object sender, EventArgs e)
{
//Is this the correct way to display a form with Autofac?
using(ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<ChildPresenter>().DisplayView(); //Display the child form
}
}
#region Implementation of IPresenter
public IView View
{
get { return view; }
}
public void DisplayView()
{
view.Display();
}
#endregion
}
public class ChildPresenter : IPresenter
{
IView view = null;
public ChildPresenter(IView view)
{
this.view = view;
}
#region Implementation of IPresenter
public IView View
{
get { return view; }
}
public void DisplayView()
{
view.Display();
}
#endregion
}
public partial class ChildView : Form, IView
{
public ChildView()
{
InitializeComponent();
}
#region Implementation of IView
public void Display()
{
Show(); //<== BUG: Child form will only flash then instantly close.
ShowDialog(); //Child form will display correctly, but since this call is modal, the parent form can't be accessed
}
#endregion
}
Look at this code:
using(ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<ChildPresenter>().DisplayView(); //Display the child form
}
First, it is good that you used a child lifetime scope. If you resolve out of the top-level container, the objects will live as long as that container (which is usually the whole lifetime of the application).
However, there is a problem here. When DisplayView calls Form.Show, it returns immediately. The using block ends, the child scope is disposed, and all of its objects (including the view) are also disposed.
In this case, you do not want a using statement. What you want to do is tie the child lifetime scope to the view so that when the view is closed, the child scope is disposed. See the FormFactory in one of my other answers for an example. There are other ways you could adapt this idea to your architecture - you could do it in the registration (ContainerBuilder) for example.
I have a ShellViewModel with a BindableCollection which is bound to a Canvas with the help of ItemControl.
I add ChildViewModel derived from Screen, to this bindableCollection with the help of a button from ShellViewModel.
I have a closebutton in the ChildViewModel, and on clicking this button, i want to remove item from BindableCollection of ShellViewModel,
Please help.
If you'd like to maintain your existing implementation, you can always create an event and use the EventAggregator. Your ChildViewModel would need to publish the event, and the ShellViewModel would need to implement the IHandle<ChildViewModelMessage> interface. As part of that implementation, it would be able to remove the ChildViewModel from the BindableCollection. Generally, it would look something like this:
public class ChildViewModelMessage {
// Implementation here
}
public class ShellViewModel : IHandle<ChildViewModelMessage> {
...
public void Handle(ChildViewModelMessage message) {
// Handle here
}
}
public class ChildViewModel {
...
public IEventAggregator Events { get; set; }
protected void HandleClose() {
this.Events.Publish(new ChildViewModelMessage());
}
If you need to talk between different viewmodels, you should use a messenger. Here's an example from the mvvm light toolkit : http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx