使用mvvm-light区分用户控件的实例

时间:2010-08-10 04:19:01

标签: user-controls mvvm-light

我有两个文件浏览器用户控件实例,控件只是一个按钮和一个文本框,但我需要两个实例,想一个diff实用程序。一旦用户为每个控件选择了一个文件,我想启用一个按钮,该按钮将对这两个文件执行操作。

我遇到的问题是如何区分控件的实例以确定是否已选择这两个文件。我想我希望我的DoSumthinViewModel只有FileChooserViewModel实现的字符串属性。

起初我有一个ViewModelLocator,它有一个属性,当访问时返回FileChooserVM的新实例,但这似乎不对,我无法区分实例。然后我沿着FileChooser的单独定位器的路径走下去,但意识到每个控件将与同一个定位器实例进行通信,从而再次与同一个FileChooserViewModel进行通信。

那么,使用同一个ViewModel的各个实例的好方法是什么?

谢谢,

Shane Holder

1 个答案:

答案 0 :(得分:0)

您的DoSomethingViewModel可能具有控件绑定的FileChooserViewModel类型的两个属性,然后检查其字符串属性中的值。

FileChooserViewModel的简化版可能是......

public class FileChooserViewModel : ViewModelBase
{
    public const string FilePathPropertyName = "FilePath";
    private string _filePath;
    public string FilePath
    {
        get { return _filePath; }
        set
        {
            if (_filePath == value) return;
            _filePath = value;
            RaisePropertyChanged(FilePathPropertyName);
            Messenger.Default.Send(new NotificationMessage("FilePath Updated"));
        }
    }
}

您的DoSomethingViewModel可能看起来像这样......

public class DoSomethingViewModel : ViewModelBase
{
    public DoSomethingViewModel()
    {
        Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
    }

    public const string FileChooser1PropertyName = "FileChooser1";
    private FileChooserViewModel _fileChooser1 = new FileChooserViewModel();
    public FileChooserViewModel FileChooser1
    {
        get { return _fileChooser1; }
        set
        {
            if (_fileChooser1 == value) return;
            _fileChooser1 = value;
            RaisePropertyChanged(FileChooser1PropertyName);
        }
    }

    public const string FileChooser2PropertyName = "FileChooser2";
    private FileChooserViewModel _fileChooser2 = new FileChooserViewModel();
    public FileChooserViewModel FileChooser2
    {
        get { return _fileChooser2; }
        set
        {
            if (_fileChooser2 == value) return;
            _fileChooser2 = value;
            RaisePropertyChanged(FileChooser2PropertyName);
        }
    }

    public const string BothFilesChosenPropertyName = "BothFilesChosen";
    public bool BothFilesChosen
    {
        get
        {
            var result = false;
            if (FileChooser1 != null && FileChooser2 != null)
                result = !string.IsNullOrWhiteSpace(FileChooser1.FilePath)
                      && !string.IsNullOrWhiteSpace(FileChooser2.FilePath);
            return result;
        }
    }

    private void NotificationMessageReceived(NotificationMessage msg)
    {
        if (msg.Sender is FileChooserViewModel)
            RaisePropertyChanged(BothFilesChosenPropertyName);
    }
}

调用NotificationMessageReceived方法,从FileChooserViewModel的FilePath属性setter发送NotificationMessage,然后它会在BothFilesChosen属性上引发属性更改事件。

<UserControl x:Class="DoSomethingProject.Views.DoSomethingView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:views="clr-namespace:DoSomethingProject.Views"
             DataContext="{Binding DoSomethingViewModel, Source={StaticResource Locator}}">
    <StackPanel>
        <views:FileChooser DataContext="{Binding Path=FileChooser1}" />
        <views:FileChooser DataContext="{Binding Path=FileChooser2}" />
        <Button IsEnabled="{Binding Path=BothFilesChosen}" />
    </StackPanel>
</UserControl>

另一种方法是在每个FileChooserViewModel属性上处理PropertyChanged事件,但我更喜欢使用消息传递,因为事件处理意味着您需要确保取消处理事件,这可能会导致内存问题变得混乱当事件被遗漏时。