سؤال

حسنًا، أود حقًا أن أعرف كيف يتعامل مطورو MVVM الخبراء مع مربع حوار مفتوح في WPF.

لا أريد حقًا القيام بذلك في ViewModel الخاص بي (حيث تتم الإشارة إلى "التصفح" عبر DelegateCommand)

void Browse(object param)
{
    //Add code here
    OpenFileDialog d = new OpenFileDialog();

    if (d.ShowDialog() == true)
    {
        //Do stuff
    }
}

لأنني أعتقد أن هذا يتعارض مع منهجية MVVM.

ماذا أفعل؟

هل كانت مفيدة؟

المحلول

وأفضل شيء نفعله هنا هو استخدام الخدمة.

وخدمة هو مجرد فئة التي يمكنك الوصول إليها من مستودع مركزي للخدمات، وغالبا ما حاوية اللجنة الأولمبية الدولية. الخدمة ثم تنفذ ما تحتاجه مثل OpenFileDialog.

وهكذا، على افتراض أن يكون لديك IFileDialogService في حاوية الوحدة، هل يمكن أن تفعل ...

void Browse(object param)
{
    var fileDialogService = container.Resolve<IFileDialogService>();

    string path = fileDialogService.OpenFileDialog();

    if (!string.IsNullOrEmpty(path))
    {
        //Do stuff
    }
}

نصائح أخرى

وكنت أود أن أعلق على أحد الأجوبة، ولكن للأسف، سمعتي ليست عالية بما فيه الكفاية للقيام بذلك.

وبعد مكالمة مثل OpenFileDialog () ينتهك نمط MVVM لأنه ينطوي على عرض (الحوار) في نموذج عرض. نموذج عرض يمكن استدعاء شيء من هذا القبيل GetFileName () (أي، إذا بسيط ملزمة ليس كافية)، ولكن يجب أن لا يهمني كيف يتم الحصول على اسم الملف.

ويمكنني استخدام الخدمة التي أنا على سبيل المثال يمكن أن تنتقل إلى منشئ بلدي viewModel أو حل عن طريق الحقن التبعية. منها مثلا.

public interface IOpenFileService
{
    string FileName { get; }
    bool OpenFileDialog()
}

وفئة تنفيذه، وذلك باستخدام OpenFileDialog تحت غطاء محرك السيارة. في viewModel، أنا فقط استخدام واجهة وبالتالي يمكن أن يسخر / استبداله إذا لزم الأمر.

ويجب أن ViewModel يتم فتح الحوارات أو حتى معرفة وجودها. إذا يقع على VM في DLL منفصل، ينبغي أن المشروع لن يكون إشارة إلى PresentationFramework.

وأود أن استخدام فئة المساعد في وجهة النظر للالحوارات المشتركة.

والطبقة المساعد تفضح أمر (ليس حدثا) الذي يربط النافذة لفي XAML. وهذا يعني استخدام RelayCommand في طريقة العرض. الطبقة المساعد هو DepencyObject لذلك يمكن ربط نموذج الرأي.

class DialogHelper : DependencyObject
{
    public ViewModel ViewModel
    {
        get { return (ViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(ViewModel), typeof(DialogHelper),
        new UIPropertyMetadata(new PropertyChangedCallback(ViewModelProperty_Changed)));

    private static void ViewModelProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (ViewModelProperty != null)
        {
            Binding myBinding = new Binding("FileName");
            myBinding.Source = e.NewValue;
            myBinding.Mode = BindingMode.OneWayToSource;
            BindingOperations.SetBinding(d, FileNameProperty, myBinding);
        }
    }

    private string FileName
    {
        get { return (string)GetValue(FileNameProperty); }
        set { SetValue(FileNameProperty, value); }
    }

    private static readonly DependencyProperty FileNameProperty =
        DependencyProperty.Register("FileName", typeof(string), typeof(DialogHelper),
        new UIPropertyMetadata(new PropertyChangedCallback(FileNameProperty_Changed)));

    private static void FileNameProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("DialogHelper.FileName = {0}", e.NewValue);
    }

    public ICommand OpenFile { get; private set; }

    public DialogHelper()
    {
        OpenFile = new RelayCommand(OpenFileAction);
    }

    private void OpenFileAction(object obj)
    {
        OpenFileDialog dlg = new OpenFileDialog();

        if (dlg.ShowDialog() == true)
        {
            FileName = dlg.FileName;
        }
    }
}

والطبقة المساعد يحتاج إلى مرجع إلى مثيل ViewModel. انظر القاموس الموارد. فقط بعد البناء، تم تعيين الخاصية ViewModel (في نفس خط XAML). هذا هو عندما منضما الخاصية FileName على الطبقة المساعد إلى الخاصية اسم الملف على نموذج الرأي.

<Window x:Class="DialogExperiment.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DialogExperiment"
        xmlns:vm="clr-namespace:DialogExperimentVM;assembly=DialogExperimentVM"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <vm:ViewModel x:Key="viewModel" />
        <local:DialogHelper x:Key="helper" ViewModel="{StaticResource viewModel}"/>
    </Window.Resources>
    <DockPanel DataContext="{StaticResource viewModel}">
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Open" Command="{Binding Source={StaticResource helper}, Path=OpenFile}" />
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>

وجود خدمة مثل الانفتاح وجهة نظر من viewmodel. لدي خاصية التبعية في الرأي، وعلى chnage للممتلكات، I فتح و FileDialog وقراءة المسار، تحديث الخاصية وبالتالي الملكية بد من VM

لقد قمت بحلها بالنسبة لي بهذه الطريقة:

  • في ViewModel لقد حددت واجهة والعمل معها في ViewModel
  • في منظر لقد قمت بتنفيذ هذه الواجهة.

لم يتم تنفيذ CommandImpl في التعليمات البرمجية أدناه.

نموذج العرض:

namespace ViewModels.Interfaces
{
    using System.Collections.Generic;
    public interface IDialogWindow
    {
        List<string> ExecuteFileDialog(object owner, string extFilter);
    }
}

namespace ViewModels
{
    using ViewModels.Interfaces;
    public class MyViewModel
    {
        public ICommand DoSomeThingCmd { get; } = new CommandImpl((dialogType) =>
        {
            var dlgObj = Activator.CreateInstance(dialogType) as IDialogWindow;
            var fileNames = dlgObj?.ExecuteFileDialog(null, "*.txt");
            //Do something with fileNames..
        });
    }
}

منظر:

namespace Views
{
    using ViewModels.Interfaces;
    using Microsoft.Win32;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;

    public class OpenFilesDialog : IDialogWindow
    {
        public List<string> ExecuteFileDialog(object owner, string extFilter)
        {
            var fd = new OpenFileDialog();
            fd.Multiselect = true;
            if (!string.IsNullOrWhiteSpace(extFilter))
            {
                fd.Filter = extFilter;
            }
            fd.ShowDialog(owner as Window);

            return fd.FileNames.ToList();
        }
    }
}

XAML:

<Window

    xmlns:views="clr-namespace:Views"
    xmlns:viewModels="clr-namespace:ViewModels"
>    
    <Window.DataContext>
        <viewModels:MyViewModel/>
    </Window.DataContext>

    <Grid>
        <Button Content = "Open files.." Command="{Binding DoSomeThingCmd}" CommandParameter="{x:Type views:OpenFilesDialog}"/>
    </Grid>
</Window>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top