如何在c#中使用MVVM(模型-视图-视图模型)编写WPF OpenFileDialog? 我已经访问了一些有关此OpenFileDialog的网站,但是我对此不清楚。这是我的代码
在 View.xaml 中:
<Window.... xmlns:VM="clr-namespace:myproject.myViewModel"
... >
<Window.DataContext><VM:myViewModel/>
</Window.DataContext>
<ItemsControl ItemsSource="{Binding mygroup}" >
<ItemsControl.ItemTemplate >
<DataTemplate>
<Grid >....
<TextBlock Text="Color" Margin="20" VerticalAlignment="Center"/>
<ComboBox KeyboardNavigation.TabIndex="0" Grid.Column="1" Margin="45,10,10,10" Height="30" Width="200" ItemsSource="{Binding Color}" />
<TextBlock Text="Shapes" Grid.Row="1" VerticalAlignment="Center" />
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="1" Grid.Row="1" Height="20" Width="150" SelectedIndex="0" HorizontalContentAlignment="Right"
VerticalAlignment="Center" ItemsSource="{Binding Shapes}">
</ComboBox>
<TextBlock Text="Open Files " VerticalAlignment="Center" Grid.Row="0" Grid.Column="2" Margin="10" />
<TextBox Grid.Column="3" Text="" Height="30" Grid.Row="0" IsReadOnly="True"
TextAlignment="Right" VerticalContentAlignment="Center" Width="200" /> <Button Grid.Column="4" Content="Browse" Height="30" VerticalAlignment="Bottom" MinWidth="41" />
</Grid>
</Window>
在 Model.cs 中:
namespace Myproject.Models
{
public class ProjectModel : INotifyPropertyChanged
{
private ObservableCollection<string> color;
private ObservableCollection<string> shapes;
public ObservableCollection<string> Color
{
get { return color; }
set
{
color = value;
NotifyPropertyChanged("Color");
}
}
public ObservableCollection<string> Shapes
{
get { return shapes; }
set
{
shapes = value;
NotifyPropertyChanged("Shapes");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
在 ViewModel.cs 中:
namespace MyProject.ViewModels
{
public class myProjectViewModel : INotifyPropertyChanged
{
private ObservableCollection<myprojectmodel> mygroup;
public ObservableCollection<myprojectmodel> Mygroup
{
get => this.mygroup;
set
{
if (Equals(value, this.mygroup)) return;
this.mygroup = value;
OnPropertyChanged();
}
}
public ProjectViewModel()
{
Mygroup = new ObservableCollection<myprojectmodel>();
List<string> lstColor = new List<string>();
lstCity = new List<string> {"Pink", "Blue", "Red"};
List<string> lstShapes = new List<string>();
lstTemperature = new List<string> {"Rectangle", "Triangle", "Circle"};
Mygroup.Add(
new ProjectModel
{
Color= new ObservableCollection<string>(lstColor),
Shapes = new ObservableCollection<string>(lstShapes),
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
我应该如何编写代码以获取OpenFileDialog。 我已经看到了此链接WPF OpenFileDialog with the MVVM pattern?,但是我不知道如何为上面的代码编写它。 请有人通过编辑代码获取OpenFileDailog来帮助我。
答案 0 :(得分:0)
我认为这种事情不属于ViewModel。这是查看特定的逻辑。 View独自处理用户输入,然后将其发送到ViewModel。 ViewModel从不要求View做某事。这将反转依赖关系链,并将ViewModel耦合到View。依赖关系必须是这样的: 查看-> ViewModel->模型。 ViewModel甚至不知道视图的类型,甚至根本不知道一个视图。
您必须从视图中打开对话框,然后将结果发送到视图模型。 为此,您可以在代码背后创建一个简单的事件处理程序,并将其附加到按钮的click事件上。您获取选择的文件并使用ICommand调用例如打开文件操作。那就是MVVM(或MVP)。将视图的关注点与模型分开。
MainWindow.xaml:
<Window x:Class="WpfOpenDialogExample.OpenFileDialogSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OpenFileDialogSample" Height="300" Width="300">
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<Grid>
<Button Name="ShowFilePickerButton" Click="ShowFilePicker_OnClick" Content="Open file" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.IO;
using System.Windows;
using Microsoft.Win32;
namespace WpfOpenDialogExample
{
public partial class OpenFileDialogSample : Window
{
public OpenFileDialogSample()
{
InitializeComponent();
}
private void ShowFilePicker_OnClick(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
OpenFileDialog openFileDialog = new OpenFileDialog();
if(openFileDialog.ShowDialog() == true && viewModel.OpenFileCommand.CanExecute(openFileDialog.FileName))
{
viewModel.OpenFileCommand.Execute(openFileDialog.FileName);
}
}
private void ShowFolderPicker_OnClick(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
FolderBrowserDialog openFolderDialog = new FolderBrowserDialog();
if(openFolderDialog.ShowDialog() == DialogResul.Ok && viewModel.OpenFolderCommand.CanExecute(openFolderDialog.SelectedPath ))
{
viewModel.OpenFolderCommand.Execute(openFolderDialog.SelectedPath );
}
}
}
}
ViewModel.cs:
public ICommand OpenFileCommand { get => new RelayCommand(OpenFile, CanOpenFile); }
private void OpenFile(string filePath)
{
...
}
private bool CanOpenFile(string filePath)
{
return File.Exists(filePath);
}
public ICommand OpenFolderCommand { get => new RelayCommand(OpenFolder, CanOpenFolder); }
private void OpenFolder(string folderPath)
{
...
}
private bool CanOpenFolder(string folderPath)
{
return Directory.Exists(filePath);
}
RelayCommand.cs:
using System;
using System.Windows.Input;
namespace WpfOpenDialogExample
{
/// <summary>
/// An implementation independent ICommand implementation.
/// Enables instant creation of an ICommand without implementing the ICommand interface for each command.
/// The individual Execute() an CanExecute() members are suplied via delegates.
/// <seealso cref="System.Windows.Input.ICommand"/>
/// </summary>
/// <remarks>The type of <c>RelaisCommand</c> actually is a <see cref="System.Windows.Input.ICommand"/></remarks>
public class RelayCommand : ICommand
{
/// <summary>
/// Default constructor to declare the concrete implementation of Execute(object):void and CanExecute(object) : bool
/// </summary>
/// <param name="executeDelegate">Delegate referencing the execution context method.
/// Delegate signature: delegate(object):void</param>
/// <param name="canExecuteDelegate">Delegate referencing the canExecute context method.
/// Delegate signature: delegate(object):bool</param>
public RelayCommand(Action<object> executeDelegate , Predicate<object> canExecuteDelegate)
{
this.executeDelegate = executeDelegate;
this.canExecuteDelegate = canExecuteDelegate;
}
/// <summary>
/// Invokes the custom <c>canExecuteDelegate</c> which should check wether the command can be executed.
/// </summary>
/// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
/// <returns>Expected to return tue, when the preconditions meet the requirements and therefore the command stored in <c>executeDelegate</c> can execute.
/// Expected to return fals when command execution is not possible.</returns>
public bool CanExecute(object parameter)
{
if (this.canExecuteDelegate != null)
{
return this.canExecuteDelegate(parameter);
}
return false;
}
/// <summary>
/// Invokes the custom <c>executeDelegate</c>, which references the command to execute.
/// </summary>
/// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
public void Execute(object parameter)
{
if (this.executeDelegate != null)
this.executeDelegate(parameter);
}
/// <summary>
/// The event is triggered every time the conditions regarding the command have changed. This occures when <c>InvalidateRequerySuggested()</c> gets explicitly or implicitly called.
/// Triggering this event usually results in an invokation of <c>CanExecute(object):bool</c> to check if the occured change has made command execution possible.
/// The <see cref="System.Windows.Input.CommandManager"/> holds a weakrefernce to the observer.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Action<object> executeDelegate;
private readonly Predicate<object> canExecuteDelegate;
}
}