3

I am new to MVVM. And i am not able to get answer of this problem since a long time. I don't know if question is so difficult or i have not explained properly. I have MainWindow.Xaml which contains a textblock and a button to receive the data from textblock and now when i press a button it should open second View called tableView.xaml (I have already created User Control for it/Xaml) .

I have two question now (Please note that i am following MVVM while answering)?

(1) How to open this View "tableView.xaml" from button click by closing the current opened MainWindow.xaml form (this button click is binded using MVVM) ?

My code for button click where this new form must open (by closing the current MainWindow.xaml) is here (so i guess the code for opening tableView.xaml must be somewhere here only):

 public void SaveExecuted() //some where here i have to open "tableView.Xaml"
 {
   System.Windows.MessageBox.Show(string.Format("Saved: {0} {1} ", DB_Col.DbName, DB_Col.NumTables));
 }
 public ICommand SavePersonCommand
        {
            get
            {
                if (savePersonCommand == null)
                    savePersonCommand = new DelegateCommand(new Action(SaveExecuted), new Func<bool>(SaveCanExecute));
                return savePersonCommand;
            }
        }

But how to do that ?

(2) The "tableView.Xaml" has to contain the GUI which will be written in c# dynamically according to the input i received in previous MainWindow.xaml on "Save Button" click and they must be added to tableView.Xaml. So how and where to write that C# code so that the GUI generated by this c# code will be rendered on tableView.Xaml ?

Summary: Could some one please let me know how to open the tableView.Xaml from Button click and how to append/render the GUI on tableView.Xaml (I know how to write the c# code to Generate GUI but where to write that code such that it will render GUI in tableView.Xaml respecting the MVVM rules).

EDIT: I feel like still dear helpers are not able to understand what i am trying to do, So please see it for more detail :

What i have to do is: I have MainWindow.xaml which contains textbox "Enter number of tables" which is receiving input from user (as Number of tables he want to create).And user after entering this input he will click save button. Now saving the button must close the MainWindow.xaml and will launch new usercontrol which is "tableView.xaml". Suppose he enter 4 and save. Now in newly launched tableView.xaml i have to show text box which will receive "Name Of table", "Nmbr of columns", if he enter 3 for Number of columns, then there should be 3 more text box to receive name of each column and data type and Primary key and save button at last.

And this GUI now has to repeat 4 times because in Mainwondow.xaml the user enter 4 in "Number of tables" option dynamically at starting. So we have to repeat this GUI 4 times for each table entry in "tableView.xaml" that's why i am writing code in c# for GUI generation and after GUI generation i will render that GUI obtained from C# code to tableView.xaml. Now you understood ? If you know any other way of doing this using MVVM then you are most welcome to give me a little sample. By generating GUI dynamically using C# code mean i have to do something like :

Now when i got Input from user in MainWindow.xaml as 4 the i repeat below GUI 4 times (in for loop inside a big container) and render it to tableView.Xaml (I am doing it in c# because i have to repeat it dynamically according to the user's choice in MainWindow.xaml form).

StackPanel stp = new StackPanel();
TextBlock txt1 = new TextBlock();
stp.Children.Add(txt1);  

This stp must go to tableView.xaml and must render 4 stackpanel each containing textblock.

struggling
  • 535
  • 1
  • 10
  • 25
  • Usually, there is main winows in applications, that should not be closed. E.g navigation system like MainWindow with frame hosting Page1, Page2, etc., or MainWindow that opens child windows and dialogs. – Liero Sep 14 '15 at 06:51

3 Answers3

2

1) You're still not being very clear in your exact requirements which makes it very difficult to answer this question. You say you want to "close the current MainWindow.xaml form" yet you seem to indicate your tableView is a UserControl. Are you trying to shut down MainWindow and replace it entirely with a completely different window? If so then what's the point? If the original MainWindow is closing why don't you just change the contents of that Window?

2) This is a big topic and again you're going to have to provide more information as to exactly what you're trying to do, but in general you use DataTemplating. You start by declaring view models for the GUI elements you wish to create, as well a parent VM that contains a list of them:

public abstract class GuiItem { } // base class

public class TextBlockVM : GuiItem { }
public class CheckBoxVM : GuiItem { }
public class TextBoxVM : GuiItem { }

public class CustomViewModel : ViewModelBase
{
    private GuiItem[] _GuiItems = new GuiItem[]
    {
        new TextBlockVM{},
        new CheckBoxVM{},
        new TextBoxVM{}
    };
    public GuiItem[] GuiItems { get { return this._GuiItems; } }
}

Then you create an ItemsControl, binding ItemsSource to the array, specifying what type of panel you want to draw them on and containing DataTemplates that specify how to template each VM in the GUI:

<ItemsControl ItemsSource="{Binding GuiItems}" Margin="10">

    <ItemsControl.Resources>

        <DataTemplate DataType="{x:Type local:TextBlockVM}">
            <TextBlock Text="This is a TextBlock" />
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:CheckBoxVM}">
            <CheckBox>This is a CheckBox</CheckBox>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:TextBoxVM}">
            <TextBox Width="100" HorizontalAlignment="Left"/>
        </DataTemplate>

    </ItemsControl.Resources>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

</ItemsControl>

Result:

enter image description here

Obviously this is a very simple example just to show the general idea, in a real-world app you'd also add fields to the view models to specify the text and command handlers etc

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
2

Ok, first of all, you should define navigation system of your app. Is it main windows with child windows? is it frame with pages? Is it main window with tabs? Then create class that will cover this logic.

E.g. to show child window from viewmodel and pass some inputs to it, you can do following:

public class MainWindowViewModel
{
    private IDialogService _dialogService;
    public MainWindowViewModel(IDialogService dialogService)
    {
        _dialogService = dialogService;
        SaveCommand = new DelegateCommand(Save);
    }

    //implement PropertyChanged if needed
    public string SomeInput { get; set; }

    public DelegateCommand SaveCommand { get; private set; }

    private void Save()
    {
        var tableViewModel = new TableViewModel();
        tableViewModel.SomeInput = this.SomeInput;

        _dialogService.ShowModal(new DialogOptions
        {
            Title = "Table view",
            Content = tableViewModel 
        });
    }
}

if you needed to navigate to another page rather than open new window, you can easily create NavigationService instead of DialogService. Idea is the same.

Why am I not creating child window directly in viewmodel? because window is view and I want to keep separation of concerns.

Why am I using IDialogService instead of concrete class? One of the MVVM principles is testability. at runtime, concrete class that opens real windows will be used, but in test I can easily create mock, that won't open windows.

In general, if you want to close window from viewmodel, you create and call some event (e.g CloseRequested) in viewmodel and window should listen to the event:

public class MainWindowViewModel
{
    public event EventHandler CloseRequested;

    private void Close()
    {
        var closeRequested = CloseRequested;
        if (closeRequested != null) closeRequested (this, EventArgs.Empty);
    }
}

//mainwinow.xaml.cs
     public MainWindow()
     {
         InitializeComponent();

         var viewModel = new MainWindowViewModel();
         viewModel.CloseRequested += (sender, args) => this.Close();
         DataContext = viewModel;
     }
Liero
  • 25,216
  • 29
  • 151
  • 297
  • I am not using any inbuilt MVVM library like (MVVM light or Prism). How to get this "DialogOptions" and "IDialogService " ? – struggling Sep 14 '15 at 11:55
  • please the question edit. for what i am trying to do . – struggling Sep 14 '15 at 12:49
  • I'm not using any library neither. I have created my own IDialogService interface and implementation. basically, its just a simple class, that shows window based on specified parameters. – Liero Sep 14 '15 at 12:54
  • @DialoService, isnt it somethign inbuilt ? But it gives unkonwn Interface.. Missing library or reference when i do in my code. But please read edit part of code, Even if it fits on my scenaro ? – struggling Sep 14 '15 at 13:04
  • IDialogService is my own interface. This in your scenario you could define IDialogService.SwitchWindows(..) methods – Liero Sep 14 '15 at 13:41
  • oK THANKS But what does this Switch window method contain ? – struggling Sep 14 '15 at 16:24
1

I believe that pure MVVM is a little too difficult and overengineered for you yet. You are probably not able to take full advantage of it.

Let's try to simplify it by combining MVVM and classical approach:

Create MainWindow.xaml, MainWindowViewModel, TableView.xaml and TableViewModel:

public class MainWindowViewModel 
{    
   //implement INotifyPropertyChanged if needed
   public int NumberOfRows { get; set; }
   public int NumberOfColumns { get; set; }

    public void Save()
    {
        //do something if needed
    }
}

MainWindow.xaml:

<StackPanel>
    <TextBlock Text="Rows" />
    <TextBox Text="{Binding NumberOfRows}" />

    <TextBlock Text="Columns" />
    <TextBox Text="{Binding NumberOfColumns}" />

    <Button Content="Save" Click="Save_Click" />
</StackPanel>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }

    MainWindowViewModel ViewModel
    {
        get { return (MainWindowViewModel)DataContext; }
    }

    private void Save_Click(object sender, RoutedEventArgs e)
    {
        ViewModel.Save();

        var tableViewWindow = new TableView(ViewModel.NumberOfRows, ViewModel.NumberOfColumns);
        this.Close();
        tableViewWindow.Show();
    }
}

TableView.xaml.cs

public partial class TableView: Window
{

    public TableView(int rows, int colums)
    {
        InitializeComponent();

        DataContext = new TableViewModel();
        //do whatever needed with rows and columns parameters. 
        //You will probably need then in TableViewModel
    }

    TableViewModel ViewModel
    {
        get { return (TableViewModel )DataContext; }
    }
}
Liero
  • 25,216
  • 29
  • 151
  • 297
  • TableView.xaml is missing and also please explain where to write c# code to generate GUI which will be rendered to TableView.xaml. And how to write tableView.xaml such that it will render the GUI passed by that c# code to it ? – struggling Sep 14 '15 at 16:29
  • I can't write entire application for you. Question was about openng window and passing parameters. The view generation can be done in TableView.xaml.cs or by binding controls like datagring to viewmodel. Ask new question if you don't know how to. – Liero Sep 14 '15 at 21:57