I am trying to nest a tab presenter in GWTP. I have one frame (North) that is tab group 1 and one frame (West) that is tab group 2. When I interact with group 1 I update the nested tab presenter which controls group 2. I have looked at the example for nested tab presenters but I have been unable to determine my error from it.
The two TabContainerPresenters are ManagmentTabsPresenter and SettingsTabsPresenter. The children of these are like the HomePresenter below. The content for these children is displayed in the center of a DockLayoutPanel. I want the ManagementTabsPresenter and the SettingsTabsPresenter to be displayed in the West slot of the same DockLayoutPanel.
The problem as I see it is that when this code is run the revealInParent() method of the ManagementTabsPresenter and the revealInParent() method of the SettingsTabsPresenter are both called whenever I try to view one on the child tabs. (i.e. homePresenter). Why are both of these revealInParent methods called? What am I missing? It is true that both SettingsTabsPresenter and ManagementTabsPresenter are in a parent tab that gets revealed. Does this mean that the revealInParent of both of these tabcontainerprestners(nonleavetabcontentproxy) will be called when the parent tabcontainerpreseter is revelatedinparent?
public class HomePresenter extends Presenter<HomePresenter.MyView, HomePresenter.MyProxy> {
#Inject
AppPlaceManager appPlaceManager;
#NameToken(NameTokens.homePage)
#ProxyStandard
#NoGatekeeper
public interface MyProxy extends TabContentProxyPlace<HomePresenter> {
}
public interface MyView extends View {
}
#TabInfo(container = ManagementTabsPresenter.class)
static TabData getTabLabel(MainAppGinjector injector) {
return new TabDataBasic(injector.getVSMMessages().home_tab(), ClientConstants.HOME_TAB_POSITION);
}
#Inject
public HomePresenter(final EventBus eventBus, final MyView view, final MyProxy proxy) {
super(eventBus, view, proxy);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, ManagementTabsPresenter.TYPE_MAIN_CONTENT_SLOT, this);
}
}
public class ManagementTabsPresenter extends TabContainerPresenter<ManagementTabsPresenter.MyView, ManagementTabsPresenter.MyProxy> {
/**
* {#link ManagementTabsPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends Proxy<ManagementTabsPresenter> {
}
/**
* {#link ManagementTabsPresenter}'s view.
*/
public interface MyView extends TabView {
}
/**
* Fired by child proxie's when their tab content is changed.
*/
#ChangeTab
public static final Type<ChangeTabHandler> TYPE_Management_ChangeTab = new Type<ChangeTabHandler>();
/**
* Use this in leaf presenters, inside their {#link #revealInParent} method.
*/
#ContentSlot
public static final GwtEvent.Type<RevealContentHandler<?>> TYPE_MAIN_CONTENT_SLOT = MainPresenter.CENTER_SLOT;
/**
* This will be the event sent to our "unknown" child presenters, in order for
* them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_Management_RequestTabs = new Type<RequestTabsHandler>();
#Inject
public ManagementTabsPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, AppPlaceManager appPlaceManager) {
super(eventBus, view, proxy,TYPE_MAIN_CONTENT_SLOT, TYPE_Management_RequestTabs, TYPE_Management_ChangeTab);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, MainPresenter.WEST_SLOT, this);
}
}
public class SettingsTabsPresenter extends TabContainerPresenter<SettingsTabsPresenter.MyView, SettingsTabsPresenter.MyProxy> {
/**
* {#link SettingsTabsPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends Proxy<SettingsTabsPresenter> {
}
/**
* {#link SettingsTabsPresenter}'s view.
*/
public interface MyView extends TabView {
}
/**
* Fired by child proxie's when their tab content is changed.
*/
#ChangeTab
public static final Type<ChangeTabHandler> TYPE_Settings_ChangeTab = new Type<ChangeTabHandler>();
/**
* Use this in leaf presenters, inside their {#link #revealInParent} method.
*/
#ContentSlot
public static final GwtEvent.Type<RevealContentHandler<?>> TYPE_MAIN_CONTENT_SLOT = MainPresenter.CENTER_SLOT;
/**
* This will be the event sent to our "unknown" child presenters, in order for
* them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_Settings_RequestTabs = new Type<RequestTabsHandler>();
#Inject
public ManagementTabsPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, AppPlaceManager appPlaceManager) {
super(eventBus, view, proxy,TYPE_MAIN_CONTENT_SLOT, TYPE_Settings_RequestTabs, TYPE_Settings_ChangeTab);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, MainPresenter.WEST_SLOT, this);
}
}
public class MainPresenter extends Presenter<MainPresenter.MyView, MainPresenter.MyProxy>{
#Inject VSMRequestFactory requestFactory;
#Inject VSMMessages vsmMessages;
#Inject VSMExceptionMessages vsmExceptionMessages;
#Inject EventBus eventBus;
#ProxyStandard
#NoGatekeeper
public interface MyProxy extends Proxy<MainPresenter> {
}
public interface MyView extends View {
}
#ContentSlot
public static final Type<RevealContentHandler<?>> SOUTH_SLOT = new Type<RevealContentHandler<?>>();
#ContentSlot
public static final Type<RevealContentHandler<?>> WEST_SLOT = new Type<RevealContentHandler<?>>();
#ContentSlot
public static final Type<RevealContentHandler<?>> CENTER_SLOT = new Type<RevealContentHandler<?>>();
#ContentSlot
public static final Type<RevealContentHandler<?>> NORTH_SLOT = new Type<RevealContentHandler<?>>();
#Inject
public MainPresenter(EventBus eventBus, MyView view, MyProxy proxy) {
super(eventBus, view, proxy);
}
#Override
protected void revealInParent() {
RevealRootLayoutContentEvent.fire(this, this);
}
#Override
protected void onReveal() {
super.onReveal();
initializeAppUser();
}
}
It should be an event issue, when you select a tab, SelectionEvent may be consumed by the wrong tab. Could you add some code about tab initialization?
Related
Hello I'm trying to setup an architecture where only one module gets booted when the app is launched. Then I'd like to lazy load other modules based on the user's actions.
To achieve this in my app.xaml.cs I have one module loaded at bootstrap time (MainModule), and an other has InitializationMode = InitializationMode.OnDemand
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
Type BlipModuleType = typeof(BlipModule);
moduleCatalog.AddModule(new ModuleInfo()
{
ModuleName = BlipModuleType.Name,
ModuleType = BlipModuleType.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand
});
moduleCatalog.AddModule<MainModule>();
}
then my main module, which displays the view correctly, has a single view registered to the only region available:
public class MainModule : IModule
{
private readonly IRegionManager _regionManager;
public MainModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewA));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
The lazy loaded module has the same structure, registering a different view (which works properly if i decide to use it as my main module)
public class BlipModule : IModule
{
private readonly IRegionManager _regionManager;
public BlipModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewB));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
finally I have a Command in the viewmodel of my MainModule ViewA, that is supposed to load the new module and navigate to it.
public class ViewAViewModel : BindableBase
{
const string BlipModuleName = "BlipModule";
public ReactiveCommand ChangeRoute { get; set; } = new ReactiveCommand();
public ViewAViewModel(IRegionManager regionManager, IModuleManager moduleManager)
{
ChangeRoute.Subscribe(res =>
{
moduleManager.LoadModule(BlipModuleName);
});
moduleManager.LoadModuleCompleted += (s, e) =>
{
if (e.ModuleInfo.ModuleName == BlipModuleName)
{
regionManager.RequestNavigate(RegionNames.ContentRegion, new Uri(BlipModuleName, UriKind.Relative));
}
};
}
}
The viewB of the BlipModule is actually loaded (I get a hit if I set a breakpoint in the view's constructor), but instead of the view I get a white page with "System.Object" inside of it.
Any idea? thanks!
You want to RegisterForNavigation instead of RegisterViewWithRegion.
My question is very similar to this thread, however it was never properly answered.
I have an ImageAdapter setup as so;
public class ImageAdapter extends BaseAdapter {
private Context mContext;
int[] mImages;
public ImageAdapter(Context c, int[] images) {
mContext = c;
mImages = images;
}
#Override
public int getCount() {
return 0;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
// if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(200, 200));
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mImages[position]);
return imageView;
}
}
And I want the images to be from an XML;
<integer-array name="images">
<item>#drawable/image1</item>
<item>#drawable/image2</item>
<item>#drawable/image3</item>
</integer-array>
In my MainActivity class I have tried to get the image array and pass it into the ImageAdapter but I can't;
public class MainActivity extends AppCompatActivity {
private int images[] = getResources().getIntArray(R.array.images);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridview = (GridView) findViewById(R.id.gridView);
gridview.setAdapter(new ImageAdapter(MainActivity.this, images));
}
}
I am currently getting an error message:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
with my app crashing on load up. I want to make my ImageAdapter display images from an XML in my GridView.
try to use
private int images[] = {R.drawable.image1, R.drawable.image3, R.drawable.image3};
instead of
private int images[] = getResources().getIntArray(R.array.images);
I would like to create a payroll program such that when the user tick a CheckBox in TableView, the name (String) will be carried on to the panel on the right and with TextFields to enter more info such as this:
I tried to follow the MVC hierarchy thus far as I code:
PayrollMainApp.java
public class PayrollMainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private ObservableList<Employee> selectEmployeeTable = FXCollections.observableArrayList();
public PayrollMainApp(){
selectEmployeeTable.add(new Employee(false,"Hans Muster"));
selectEmployeeTable.add(new Employee(true,"Ruth Mueller"));
selectEmployeeTable.add(new Employee(false,"Heinz Kurz"));
selectEmployeeTable.add(new Employee(false,"Cornelia Meier"));
selectEmployeeTable.add(new Employee(false,"Werner Meyer"));
selectEmployeeTable.add(new Employee(false,"Lydia Kunz"));
selectEmployeeTable.add(new Employee(false,"Anna Best"));
selectEmployeeTable.add(new Employee(false,"Stefan Meier"));
selectEmployeeTable.add(new Employee(false,"Martin Mueller"));
}
public ObservableList<Employee> getSelectEmployeeTable(){
return selectEmployeeTable;
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("PayrollApp");
initRootLayout();
showEmployeeOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PayrollMainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the person overview inside the root layout.
*/
public void showEmployeeOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PayrollMainApp.class.getResource("view/EmployeeOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(personOverview);
// Give the controller access to the main app
EmployeeOverviewController controller = loader.getController();
controller.setMainApp(this);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
* #return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
Employee.java
public class Employee {
private BooleanProperty checkedBox = new SimpleBooleanProperty(false);
private StringProperty employeeName = new SimpleStringProperty();
public Employee(){
super();
}
public Employee(boolean checkedBox, String employeeName){
this.checkedBox = new SimpleBooleanProperty(false);
this.employeeName = new SimpleStringProperty(employeeName);
}
public BooleanProperty checkedBoxProperty(){
return this.checkedBox;
}
public StringProperty employeeNameProperty(){
return this.employeeName;
}
public java.lang.Boolean getSelectBox() {
return this.checkedBoxProperty().get();
}
public StringProperty getEmployeeName() {
return employeeName;
}
public void setSelectBox(final java.lang.Boolean checkedBox){
this.checkedBoxProperty().set(checkedBox);
}
public void setEmployeeName(StringProperty employeeName) {
this.employeeName = employeeName;
}
}
EmployeeOverviewController.java
public class EmployeeOverviewController {
#FXML
private TableView<Employee> selectEmployeeTable;
#FXML
private TableColumn<Employee, String> employeeNameColumn;
#FXML
private TableColumn<Employee, Boolean> checkBoxColumn;
private PayrollMainApp mainApp;
public EmployeeOverviewController() {
}
#FXML
public void initialize() {
checkBoxColumn.setCellValueFactory(cellData -> cellData.getValue().checkedBoxProperty());
checkBoxColumn.setCellFactory(param -> new CheckBoxTableCell<Employee, Boolean>());
employeeNameColumn.setCellValueFactory(cellData -> cellData.getValue().employeeNameProperty());
}
public void setMainApp(PayrollMainApp mainApp){
this.mainApp = mainApp;
//Add observable list data to the table
selectEmployeeTable.setItems(mainApp.getSelectEmployeeTable());
}
}
And a util class to make the checkBox visible in the table:
SelectBoxCellFactory.java
public class SelectBoxCellFactory implements Callback {
#Override
public TableCell call(Object param) {
CheckBoxTableCell<Employee,Boolean> checkBoxCell = new CheckBoxTableCell();
return checkBoxCell;
}
}
Here is my output thus far:
I know this has a table in it as compared to the previous output. Honestly I'm still indecisive as to use which, because I think using TextFields would make it look better. But all I hope for now is that this design is not impossible to code...
I really hope you can help me... Thank you for your help in advance.
It's probably easiest to use a TableView for the right panel. You can create a FilteredList from your original list:
FilteredList<Employee> selectedEmployees
= new FilteredList<>(selectEmployeeTable, Employee::getSelectBox);
and then use that for your second table.
If you prefer to use text fields (in what looks like a GridPane?) you can still use the filtered list above, but you will need to register a listener with it and update the layout "by hand" when items are added and removed.
I posted a question the other day and didn't get much response. I have tried a few things but still no luck. I am trying to nest a tab presenter in GWTP. I have one frame (North) that is tab group 1 and one frame (West) that is tab group 2. When I interact with group 1 I update the nested tab presenter which controls group 2. I have looked at the example for nested tab presenters but I have been unable to determine my error from it.
The two TabContainerPresenters are ManagementTabsPresenter and SettingsTabsPresenter. The children of these are like the HomePresenter below. The content for these children is displayed in the center of a DockLayoutPanel located in the MainPreseter. I want the ManagementTabsPresenter and the SettingsTabsPresenter to be displayed in the West slot of the same DockLayoutPanel.
When I run this code everything is placed on the screen in the correct locations. You can even click the tabs and have actions occur. The problems start in that the SettingsPresenter is displayed first even though the default page is the HomePresenter. It's like it tries to reveal both at the same time on start up. Then when you selected any of the tabs which are part of the ManagementTabsPresenter or the SettingsTabsPresenter It opens the correct content in the MainPresenter.Center_Slot but the incorrect presenter is displayed in the MainPresenter.West_Slot. I can't figure out what's going wrong here. Any help would be appreciated.
Here is the code:
public class HomePresenter extends Presenter<HomePresenter.MyView, HomePresenter.MyProxy> {
#Inject
AppPlaceManager appPlaceManager;
#NameToken(NameTokens.homePage)
#ProxyStandard
#NoGatekeeper
public interface MyProxy extends TabContentProxyPlace<HomePresenter> {
}
public interface MyView extends View {
}
#TabInfo(container = ManagementTabsPresenter.class)
static TabData getTabLabel(MainAppGinjector injector) {
return new TabDataBasic("home", 0);
}
#Inject
public HomePresenter(final EventBus eventBus, final MyView view, final MyProxy proxy) {
super(eventBus, view, proxy);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, ManagementTabsPresenter.TYPE_MAIN_CONTENT_SLOT, this);
}
}
public class ManagementTabsPresenter extends TabContainerPresenter<ManagementTabsPresenter.MyView, ManagementTabsPresenter.MyProxy> {
/**
* {#link ManagementTabsPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends NonLeafTabContentProxy<ManagementTabsPresenter> {
}
/**
* {#link ManagementTabsPresenter}'s view.
*/
public interface MyView extends TabView {
}
/**
* Use this in leaf presenters, inside their {#link #revealInParent} method.
*/
#ContentSlot
public static final GwtEvent.Type<RevealContentHandler<?>> TYPE_MAIN_CONTENT_SLOT = MainPresenter.CENTER_SLOT;
/**
* This will be the event sent to our "unknown" child presenters, in order for
* them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_Management_RequestTabs = new Type<RequestTabsHandler>();
#TabInfo(container = HeaderTabsPresenter.class, nameToken = NameTokens.homePage)
static TabData getTabLabel(MainAppGinjector injector) {
return new TabDataBasic("Management", 0);
}
#Inject
public ManagementTabsPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, AppPlaceManager appPlaceManager) {
super(eventBus, view, proxy,TYPE_MAIN_CONTENT_SLOT, TYPE_Management_RequestTabs);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, HeaderTabsPresenter.TYPE_VERTICAL_TABS_SLOT, this);
}
}
public class SettingsTabsPresenter extends TabContainerPresenter<SettingsTabsPresenter.MyView, SettingsTabsPresenter.MyProxy> {
/**
* {#link SettingsTabsPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends NonLeafTabContentProxy<SettingsTabsPresenter> {
}
/**
* {#link SettingsTabsPresenter}'s view.
*/
public interface MyView extends TabView {
}
#TabInfo(container = HeaderTabsPresenter.class, nameToken = NameTokens.appUserCollectionPage)
static TabData getTabLabel(MainAppGinjector injector) {
return new TabDataBasic("Settings", 1);
}
/**
* Use this in leaf presenters, inside their {#link #revealInParent} method.
*/
#ContentSlot
public static final GwtEvent.Type<RevealContentHandler<?>> TYPE_MAIN_CONTENT_SLOT = MainPresenter.CENTER_SLOT;
/**
* This will be the event sent to our "unknown" child presenters, in order for
* them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_Settings_RequestTabs = new Type<RequestTabsHandler>();
#Inject
public SettingsTabsPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, AppPlaceManager appPlaceManager) {
super(eventBus, view, proxy,TYPE_MAIN_CONTENT_SLOT, TYPE_Settings_RequestTabs);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, HeaderTabsPresenter.TYPE_VERTICAL_TABS_SLOT, this);
}
}
public class HeaderTabsPresenter extends TabContainerPresenter<HeaderTabsPresenter.MyView, HeaderTabsPresenter.MyProxy> {
#Inject EventBus eventBus;
/**
* {#link HeaderTabsPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends Proxy<HeaderTabsPresenter> {
}
/**
* {#link HeaderTabsPresenter}'s view.
*/
public interface MyView extends TabView {
void changeTab(Tab tab, TabData tabData, String historyToken);
}
/**
* This will be the event sent to our "unknown" child presenters, in order for
* them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_HEADER_RequestTabs = new Type<RequestTabsHandler>();
/**
* Fired by child proxie's when their tab content is changed.
*/
#ChangeTab
public static final Type<ChangeTabHandler> TYPE_HEADER_ChangeTab = new Type<ChangeTabHandler>();
#ContentSlot
public static final Type<RevealContentHandler<?>> TYPE_VERTICAL_TABS_SLOT = MainPresenter.WEST_SLOT;
#Inject
public HeaderTabsPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy) {
super(eventBus, view, proxy, TYPE_VERTICAL_TABS_SLOT, TYPE_HEADER_RequestTabs, TYPE_HEADER_ChangeTab);
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, MainPresenter.NORTH_SLOT, this);
}
}
public class MainPresenter extends Presenter<MainPresenter.MyView, MainPresenter.MyProxy>{
#Inject VSMRequestFactory requestFactory;
#Inject VSMMessages vsmMessages;
#Inject VSMExceptionMessages vsmExceptionMessages;
#Inject EventBus eventBus;
#ProxyStandard
#NoGatekeeper
public interface MyProxy extends Proxy<MainPresenter> {
}
public interface MyView extends View {
}
#ContentSlot
public static final Type<RevealContentHandler<?>> SOUTH_SLOT = new Type<RevealContentHandler<?>>();
#ContentSlot
public static final Type<RevealContentHandler<?>> WEST_SLOT = new Type<RevealContentHandler<?>>();
#ContentSlot
public static final Type<RevealContentHandler<?>> CENTER_SLOT = new Type<RevealContentHandler<?>>();
#ContentSlot
public static final Type<RevealContentHandler<?>> NORTH_SLOT = new Type<RevealContentHandler<?>>();
#Inject
public MainPresenter(EventBus eventBus, MyView view, MyProxy proxy) {
super(eventBus, view, proxy);
}
#Override
protected void revealInParent() {
RevealRootLayoutContentEvent.fire(this, this);
}
#Override
protected void onReveal() {
super.onReveal();
initializeAppUser();
}
}
I stumbled upon something similar before... I think it is because both of them inherit from TabContainerPresenter and that there is a bug in gwtp that calls onReveal() on the wrong inherited child.
take a look at this gwtp forum post:
https://groups.google.com/forum/?fromgroups#!searchin/gwt-platform/gilad$20egozi/gwt-platform/p0s3BlN-ceE/5EL3nynaiu4J
Is that the case? if so, I don't think anyone filed a bug on the gwtp issue tracker, so let me know.
(of-course, the best check is not to inherit and see if its working)
I haveViewModel1 and View1 associated with it. I start dialog window from ViewModel2 (some another viewmodel) using IWindowManager object. The code from ViewModel2 class:
windowManager.ShowDialog(new ViewModel());
So, I have Dialog Window with View1 user control.
My answer is next - I can close that dialog window using red close button, but how to close it using my specific button (contained in View1 user control), something like "Cancel" button with close command (Command={Binding CancelCommand}), CancelCommand of course is contained in ViewModel1 class.
It's even easier if your view model extends Caliburn.Micro.Screen:
TryClose();
You can get the current view (in your case the dialog window) with implementing the IViewAware interface on your ViewModel. Then you can call Close on the the view (the Window created as the dialog) when your command is executed.
The easiest why is to derive from ViewAware:
public class DialogViewModel : ViewAware
{
public void ExecuteCancelCommand()
{
(GetView() as Window).Close();
}
}
If you are not allowed to derive you can implement it yourself:
public class DialogViewModel : IViewAware
{
public void ExecuteCancelCommand()
{
dialogWindow.Close();
}
private Window dialogWindow;
public void AttachView(object view, object context = null)
{
dialogWindow = view as Window;
if (ViewAttached != null)
ViewAttached(this,
new ViewAttachedEventArgs(){Context = context, View = view});
}
public object GetView(object context = null)
{
return dialogWindow;
}
public event EventHandler<ViewAttachedEventArgs> ViewAttached;
}
Note: I've used Caliburn.Micro 1.3.1 for my sample.
A cleaner way (Subject of personal taste) that I use alot is to use the IResult pattern, this way you abstract the Window implemenation
Viewmodel
public IEnumerable<IResult> CloseMe()
{
yield return new CloseResult();
}
Result code
public class CloseResult : Result
{
public override void Execute(ActionExecutionContext context)
{
var window = Window.GetWindow(context.View);
window.Close();
base.Execute(context);
}
}
public abstract class Result : IResult
{
public virtual void Execute(ActionExecutionContext context)
{
OnCompleted(this, new ResultCompletionEventArgs());
}
protected virtual void OnCompleted(object sender, ResultCompletionEventArgs e)
{
if (Completed != null)
Completed(sender, e);
}
public event EventHandler<ResultCompletionEventArgs> Completed;
}
edit (Only needed for IoC): If you wanna take it a step further you do a base class for all screens
public abstract class ShellPresentationModel : Screen
{
public ShellPresentationModel(IResultFactory resultFactory)
{
Result = resultFactory;
}
public IResultFactory Result { get; private set; }
}
This way you can inject dependencies with a IoC much easier, then your VIewmodel close method will look like this
public IEnumerable<IResult> CloseMe()
{
yield return Result.Close();
}
An example on a IResult that uses dependency can be
public class ShowDialogResult<TModel> : Result
{
private readonly IWindowManager windowManager;
private readonly TModel model;
private Action<TModel> configure;
public ShowDialogResult(IWindowManager windowManager, TModel model)
{
this.windowManager = windowManager;
this.model = model;
}
public IResult Configure(Action<TModel> configure)
{
this.configure = configure;
return this;
}
public override void Execute(ActionExecutionContext context)
{
if(configure != null)
configure(model);
windowManager.ShowDialog(model);
base.Execute(context);
}
}
edit Just noticed that i forgot to add an example of the above IoC exmaple, here goes
With a child IoC container pattern it would look like this
public IEnumerable<IResult> ShowDialog()
{
yield return Result.ShowDialog<MyViewModel>();
}
Without a child container pattern you would need to inject parent dependeync into the child manually
yield return Result.ShowDialog<MyViewModel>().Configure(m => m.SomeData = this.SomeData);