MVVM 패턴이 있는 WPF OpenFileDialog?
저는 이제 막 WPF의 MVVM 패턴을 배우기 시작했습니다.저는 벽에 부딪혔습니다. 보여주어야 할 때는 어떻게 해야 합니까?
다음은 사용하려는 UI의 예입니다.

찾아보기 버튼을 클릭하면OpenFileDialog표시되어야 합니다.사용자가 파일을 선택할 때OpenFileDialog텍스트 상자에 파일 경로가 표시되어야 합니다.
MVVM으로 이 작업을 수행하려면 어떻게 해야 합니까?
업데이트: MVVM으로 이 작업을 수행하고 유닛을 테스트할 수 있도록 하려면 어떻게 해야 합니까?아래 솔루션은 유닛 테스트에 적용되지 않습니다.
제가 일반적으로 하는 일은 이 기능을 수행하는 애플리케이션 서비스를 위한 인터페이스를 만드는 것입니다.제 예에서는 MVVM 툴킷이나 유사한 툴킷을 사용하고 있다고 가정하겠습니다(기본 View Model과 a-based View Model을 사용할 수 있습니다).RelayCommand).
다음은 다음과 같은 기본 IO 작업을 수행하기 위한 매우 단순한 인터페이스의 예입니다.OpenFileDialog그리고.OpenFile이 두 가지를 모두 보여드리겠습니다. 따라서 이 문제를 해결하기 위해 하나의 방법으로 하나의 인터페이스를 만들자는 제안을 하지 않을 것입니다.
public interface IOService
{
string OpenFileDialog(string defaultPath);
//Other similar untestable IO operations
Stream OpenFile(string path);
}
응용 프로그램에서 이 서비스의 기본 구현을 제공합니다.여기 당신이 그것을 소비하는 방법이 있습니다.
public MyViewModel : ViewModel
{
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set { _selectedPath = value; OnPropertyChanged("SelectedPath"); }
}
private RelayCommand _openCommand;
public RelayCommand OpenCommand
{
//You know the drill.
...
}
private IOService _ioService;
public MyViewModel(IOService ioService)
{
_ioService = ioService;
OpenCommand = new RelayCommand(OpenFile);
}
private void OpenFile()
{
SelectedPath = _ioService.OpenFileDialog(@"c:\Where\My\File\Usually\Is.txt");
if(SelectedPath == null)
{
SelectedPath = string.Empty;
}
}
}
이것은 매우 간단합니다.이제 마지막 부분은 테스트 가능성입니다.이것은 분명해야 하지만, 이것에 대한 간단한 테스트를 만드는 방법을 보여드리겠습니다.저는 그루터기로 모크를 사용하지만, 물론 당신이 원하는 것은 무엇이든 사용할 수 있습니다.
[Test]
public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty()
{
Mock<IOService> ioServiceStub = new Mock<IOService>();
//We use null to indicate invalid path in our implementation
ioServiceStub.Setup(ioServ => ioServ.OpenFileDialog(It.IsAny<string>()))
.Returns(null);
//Setup target and test
MyViewModel target = new MyViewModel(ioServiceStub.Object);
target.OpenCommand.Execute();
Assert.IsEqual(string.Empty, target.SelectedPath);
}
이것은 아마 당신에게 효과가 있을 것입니다.
CodePlex에는 "SystemWrapper"(http://systemwrapper.codeplex.com )라는 라이브러리가 있어 이러한 작업을 많이 수행하지 않아도 됩니다.처럼 보입니다.FileDialog아직 지원되지 않으므로 해당 인터페이스를 작성해야 합니다.
편집:
당신이 가짜 프레임워크를 위해 타입모크 아이솔레이터를 선호했던 것으로 기억합니다.다음은 아이솔레이터를 사용한 동일한 테스트입니다.
[Test]
[Isolated]
public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty()
{
IOService ioServiceStub = Isolate.Fake.Instance<IOService>();
//Setup stub arrangements
Isolate.WhenCalled(() => ioServiceStub.OpenFileDialog("blah"))
.WasCalledWithAnyArguments()
.WillReturn(null);
//Setup target and test
MyViewModel target = new MyViewModel(ioServiceStub);
target.OpenCommand.Execute();
Assert.IsEqual(string.Empty, target.SelectedPath);
}
WPF(WPF 응용 프로그램) Framework)는 Open and SaveFileDialog에 대한 구현을 제공합니다.
Writer 샘플 응용 프로그램은 사용 방법과 코드를 단위로 테스트할 수 있는 방법을 보여줍니다.
먼저 WPF MVVM 툴킷으로 시작하는 것을 추천합니다.이렇게 하면 프로젝트에 사용할 명령을 쉽게 선택할 수 있습니다.MVVM 패턴이 도입된 이후로 유명해진 한 가지 특별한 기능은 릴레이 명령입니다(물론 매니의 다른 버전도 있지만 저는 가장 일반적으로 사용되는 기능만 고수합니다).View 모델에서 새 명령을 생성할 수 있는 IC 명령 인터페이스의 구현입니다.
질문으로 돌아가서, View 모델의 모양에 대한 예를 들어 보겠습니다.
public class OpenFileDialogVM : ViewModelBase
{
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
RaisePropertyChanged("SelectedPath");
}
}
private string _defaultPath;
public OpenFileDialogVM()
{
RegisterCommands();
}
public OpenFileDialogVM(string defaultPath)
{
_defaultPath = defaultPath;
RegisterCommands();
}
private void RegisterCommands()
{
OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
}
private void ExecuteOpenFileDialog()
{
var dialog = new OpenFileDialog { InitialDirectory = _defaultPath };
dialog.ShowDialog();
SelectedPath = dialog.FileName;
}
}
View ModelBase 및 RelayCommand는 모두 MVVM 툴킷에서 제공됩니다.이것이 XAML의 모습입니다.
<TextBox Text="{Binding SelectedPath}" />
<Button Command="vm:OpenFileDialogVM.OpenCommand" >Browse</Button>
그리고 당신의 XAML.CS 코드가 뒤에 있습니다.
DataContext = new OpenFileDialogVM();
InitializeComponent();
바로 그거야.
명령에 익숙해질수록 찾아보기 단추를 비활성화할 시기 등에 대한 조건을 설정할 수도 있습니다.그것이 당신이 원하는 방향을 가리켰기를 바랍니다.
제 생각에 가장 좋은 해결책은 사용자 지정 컨트롤을 만드는 것입니다.
내가 주로 만드는 사용자 지정 컨트롤은 다음과 같이 구성됩니다.
- 텍스트 상자 또는 텍스트 블록
- 이미지를 템플릿으로 사용하는 버튼
- 파일 경로를 래핑할 문자열 종속성 속성
그래서 *.xaml 파일은 다음과 같습니다.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Button Grid.Column="1" Click="Button_Click">
<Button.Template>
<ControlTemplate>
<Image Grid.Column="1" Source="../Images/carpeta.png"/>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
그리고 *.cs 파일:
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
typeof(string),
typeof(customFilePicker),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal));
public string Text
{
get
{
return this.GetValue(TextProperty) as String;
}
set
{
this.SetValue(TextProperty, value);
}
}
public FilePicker()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if(openFileDialog.ShowDialog() == true)
{
this.Text = openFileDialog.FileName;
}
}
마지막으로 뷰 모델에 바인딩할 수 있습니다.
<controls:customFilePicker Text="{Binding Text}"/>
제 관점에서 가장 좋은 옵션은 프리즘 라이브러리와 상호 작용 요청입니다.대화 상자를 여는 작업은 xaml 내에서 유지되며 View 모델에서 트리거되지만 View 모델은 뷰에 대해 아무것도 알 필요가 없습니다.
참고 항목
https://plainionist.github.io/ //Mvvm-Dialogs/
예를 들어 다음을 참조하십시오.
언급URL : https://stackoverflow.com/questions/1619505/wpf-openfiledialog-with-the-mvvm-pattern
'programing' 카테고리의 다른 글
| URL에서 파일 확장자를 가져올 수 있는 방법이 있습니까? (0) | 2023.05.11 |
|---|---|
| XAML에서 색상을 브러시로 변환하려면 어떻게 해야 합니까? (0) | 2023.05.06 |
| Git에서 HEAD는 무엇입니까? (0) | 2023.05.06 |
| 범위에서 이름을 반환하려면 어떻게 해야 합니까? (0) | 2023.05.06 |
| PostgreSQL에서 임시 함수를 만드는 방법은 무엇입니까? (0) | 2023.05.06 |