programing

MVVM 패턴이 있는 WPF OpenFileDialog?

mailnote 2023. 5. 6. 15:19
반응형

MVVM 패턴이 있는 WPF OpenFileDialog?

저는 이제 막 WPF의 MVVM 패턴을 배우기 시작했습니다.저는 벽에 부딪혔습니다. 보여주어야때는 어떻게 해야 합니까?

다음은 사용하려는 UI의 예입니다.

alt 텍스트

찾아보기 버튼을 클릭하면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/

예를 들어 다음을 참조하십시오.

https://github.com/plainionist/Plainion.Prism/blob/master/src/Plainion.Prism/Interactivity/PopupCommonDialogAction.cs

https://github.com/plainionist/Plainion.Prism/blob/master/src/Plainion.Prism/Interactivity/InteractionRequest/OpenFileDialogNotification.cs

언급URL : https://stackoverflow.com/questions/1619505/wpf-openfiledialog-with-the-mvvm-pattern

반응형