Как открыть один вид из другого, используя MVVM в WPF
Я новичок в MVVM. И я не могу получить ответ на эту проблему в течение длительного времени. Я не знаю, так ли сложен вопрос или я не объяснил должным образом. У меня есть MainWindow.Xaml, который содержит текстовый блок и кнопку для получения данных из текстового блока, и теперь, когда я нажимаю кнопку, он должен открыть второй View с именем tableView.xaml (я уже создал пользовательский элемент управления для него /Xaml) .
У меня есть два вопроса сейчас (обратите внимание, что я следую MVVM, отвечая)?
(1) Как открыть это представление "tableView.xaml" от нажатия кнопки, закрыв текущую открытую форму MainWindow.xaml (нажатие этой кнопки привязывается с помощью MVVM)?
Мой код для нажатия кнопки, где эта новая форма должна открываться (закрывая текущий файл MainWindow.xaml), находится здесь (поэтому я предполагаю, что код для открытия tableView.xaml должен быть где-то здесь только):
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;
}
}
Но как это сделать?
(2) "tableView.Xaml" должен содержать графический интерфейс, который будет динамически записываться на C# в соответствии с вводом, полученным мной в предыдущем MainWindow.xaml при нажатии кнопки "Сохранить", и они должны быть добавлены в tableView.Xaml. Так как и где написать этот код C#, чтобы графический интерфейс, сгенерированный этим кодом C#, отображался в tableView.Xaml?
Резюме: Может кто-нибудь, пожалуйста, дайте мне знать, как открыть tableView.Xaml от нажатия кнопки и как добавить / визуализировать GUI на tableView.Xaml (я знаю, как написать код C# в Generate GUI, но где написать этот код, такой что он будет визуализировать GUI в tableView.Xaml с соблюдением правил MVVM).
РЕДАКТИРОВАТЬ: я чувствую, что все еще дорогие помощники не в состоянии понять, что я пытаюсь сделать, поэтому, пожалуйста, посмотрите это для более подробной информации:
Что мне нужно сделать, это: У меня есть MainWindow.xaml, который содержит текстовое поле "Введите количество таблиц", который получает ввод от пользователя (как количество таблиц, которые он хочет создать). И пользователь после ввода этого ввода он нажмет кнопку "Сохранить". Теперь сохранение кнопки должно закрыть MainWindow.xaml и запустить новый usercontrol, который является "tableView.xaml". Предположим, он ввел 4 и сохрани. Теперь во вновь запущенном tableView.xaml я должен показать текстовое поле, которое получит "Имя таблицы", "Nmbr столбцов", если он введет 3 для Количество столбцов, то должно быть еще 3 текстовых поля для получения имени каждого столбец и тип данных и первичный ключ и кнопка сохранения наконец.
И этот GUI теперь должен повторяться 4 раза, потому что в Mainwondow.xaml пользователь вводит 4 в параметре "Количество таблиц" динамически при запуске. Поэтому мы должны повторить этот GUI 4 раза для каждой записи таблицы в "tableView.xaml", поэтому я пишу код на C# для генерации GUI, и после генерации GUI я буду отображать этот GUI, полученный из кода C#, в tableView.xaml. Теперь ты понял? Если вы знаете какой-либо другой способ сделать это с помощью MVVM, тогда вы можете дать мне небольшой пример. Динамически генерируя GUI с использованием кода на C#, я должен сделать что-то вроде:
Теперь, когда я получил Ввод от пользователя в MainWindow.xaml как 4, я повторяю ниже GUI 4 раза (в цикле for внутри большого контейнера) и отображаю его в tableView.Xaml (я делаю это в C#, потому что я должен динамически повторять его по выбору пользователя в форме MainWindow.xaml).
StackPanel stp = new StackPanel();
TextBlock txt1 = new TextBlock();
stp.Children.Add(txt1);
Этот stp должен идти в tableView.xaml и должен отображать 4 стека, каждая из которых содержит текстовый блок.
3 ответа
1) Вы все еще не очень четко понимаете свои точные требования, что затрудняет ответ на этот вопрос. Вы говорите, что хотите "закрыть текущую форму MainWindow.xaml", но, похоже, указываете, что ваш tableView является UserControl. Вы пытаетесь закрыть MainWindow и полностью заменить его другим окном? Если так, то какой смысл? Если оригинальное MainWindow закрывается, почему бы вам просто не изменить содержимое этого окна?
2) Это большая тема, и вам снова нужно будет предоставить больше информации о том, что именно вы пытаетесь сделать, но в целом вы используете DataTemplating. Вы начинаете с объявления моделей представления для элементов GUI, которые вы хотите создать, а также родительской виртуальной машины, которая содержит их список:
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; } }
}
Затем вы создаете ItemsControl, связывая ItemsSource с массивом, определяя, на какой тип панели вы хотите рисовать их, и содержащие шаблоны данных, которые указывают, как шаблонировать каждую виртуальную машину в графическом интерфейсе:
<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>
Результат:
Очевидно, что это очень простой пример, просто чтобы показать общую идею, в реальном приложении вы также добавили бы поля к моделям представления, чтобы указать текст и обработчики команд и т. Д.
Хорошо, прежде всего, вы должны определить навигационную систему вашего приложения. Это главные окна с дочерними окнами? это кадр со страницами? Это главное окно с вкладками? Затем создайте класс, который будет охватывать эту логику.
Например, чтобы показать дочернее окно из viewmodel и передать ему некоторые входные данные, вы можете сделать следующее:
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
});
}
}
Если вам нужно перейти на другую страницу, а не открыть новое окно, вы можете легко создать NavigationService вместо DialogService. Идея такая же.
Почему я не создаю дочернее окно непосредственно в viewmodel? потому что окно это вид, и я хочу сохранить разделение интересов.
Почему я использую IDialogService
а не конкретный класс? Одним из принципов MVVM является тестируемость. во время выполнения будет использоваться конкретный класс, который открывает реальные окна, но в тесте я легко могу создать макет, который не будет открывать окна.
В общем, если вы хотите закрыть окно из viewmodel, вы создаете и вызываете какое-то событие (например, CloseRequested) в viewmodel, и окно должно прослушивать событие:
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;
}
Я считаю, что чистый MVVM пока слишком сложен и слишком силен для вас. Вы, вероятно, не в состоянии в полной мере воспользоваться этим.
Попробуем упростить его, объединив MVVM и классический подход:
Создайте MainWindow.xaml, MainWindowViewModel, TableView.xaml и 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; }
}
}