将ViewModel引用传递给另一个ViewModel(与View绑定的引用相同)?

时间:2015-09-15 20:56:57

标签: c# wpf mvvm

所以我有一个包含两个子视图的视图。其中一个子视图是带有文本框的屏幕键盘。下面是一些按钮,它们是不同子视图的一部分。见下文:

keyboard and buttons

当我按下键盘按钮时,它会在文本框中输入。带按钮的子视图和带键盘的子视图都有自己的ViewModel。我的问题是,如何从按钮视图中引用键盘视图(例如,我可以获取文本字段的内容,或者如果用户单击“返回”则清除它。)

我正在尝试将其概念化,但我无法弄清楚如何获得主视图所具有的键盘ViewModel的相同实例。

我可以创建一个变量:

private KeyboardViewModel keyboard;

但是如何使用Main View已经拥有的实例来实例化该变量(所以我可以从按钮viewmodel访问这些属性)?

3 个答案:

答案 0 :(得分:3)

主要问题是,当实际需要在多个View / ViewModel中重用数据源时,您错误地放置了一个ViewModel中的数据源。您需要做的是将数据源重构为单个实例或可以注入到不同ViewModel构造函数中的单独实例。通过从特定ViewModel中分离出数据源,可以为不同的访问位置提供自由。

public class DataCache
{
    private static DataCache singletonInstance;

    // You can have freedom to choose the event-driven model here
    // Using traditional Event, EventAggregator, ReactiveX, etc
    public EventHandler OnMessageChanged;

    private DataCache()
    {

    }

    public static DataCache Instance
    {
        get { return singletonInstance ?? (singletonInstance = new DataCache()); }
    }

    public string OnScreenMessage { get; set; }

    public void AddStringToMessage(string c)
    {
        if (string.IsNullOrWhiteSpace(c)) return;

        OnScreenMessage += c;
        RaiseOnMessageChanged();
    }

    public void ClearMessage()
    {
        OnScreenMessage = string.Empty;
        RaiseOnMessageChanged();
    }

    private void RaiseOnMessageChanged()
    {
        if (OnMessageChanged != null)
            OnMessageChanged(null, null);            
    }
}

public class MainViewModel : ViewModelBase
{
    private readonly MessageViewModel messageVM;
    private readonly KeyboardViewModel keyboardVM;
    private readonly ButtonsViewModel buttonsVM;

    private readonly DataCache dataCache;

    public MainViewModel()
    {
        messageVM = new MessageViewModel();
        keyboardVM = new KeyboardViewModel();
        buttonsVM = new ButtonsViewModel();
    }

    public ViewModelBase MessageViewModel { get { return messageVM; } }
    public ViewModelBase KeyboardViewModel { get { return keyboardVM;  } }
    public ViewModelBase ButtonsViewModel { get { return buttonsVM; } }
}

public class MessageViewModel : ViewModelBase
{
    private readonly DataCache dataCache = DataCache.Instance;

    public MessageViewModel()
    {
        dataCache.OnMessageChanged += RaiseMessageChanged;
    }

    private void RaiseMessageChanged(object sender, EventArgs e)
    {
        OnPropertyChanged("Message");
    }

    public string Message
    {
        get { return dataCache.OnScreenMessage; }
        set { dataCache.OnScreenMessage = value; }
    }
}

public class KeyboardViewModel : ViewModelBase
{
    private readonly DataCache dataCache = DataCache.Instance;

    private ICommand onClickButtonCommand;
    public ICommand OnClickButton
    {
        get
        {
            return onClickButtonCommand ?? (onClickButtonCommand = new RelayCommand(p => dataCache.AddStringToMessage((string)p))); 
        }
    }
}

public class ButtonsViewModel : ViewModelBase
{
    private readonly DataCache dataCache = DataCache.Instance;

    private ICommand onGoBackCommand;
    public ICommand OnGoBackButton
    {
        get
        {
            return onGoBackCommand ?? (onGoBackCommand = new RelayCommand(p => dataCache.ClearMessage()));
        }
    }
}

public class RelayCommand : ICommand
{
    #region Fields

    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    #endregion Fields

    #region Constructors

    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var handler = CanExecuteChanged;
        if (handler != null) handler(this, EventArgs.Empty);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }


    #endregion ICommand Members
}

<Window x:Class="StudentScoreWpfProj.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:StudentScoreWpfProj"        
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance Type=local:MainViewModel,IsDesignTimeCreatable=True}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <local:MessgaeView DataContext="{Binding MessageViewModel}" />
    <local:KeyboardView Grid.Row="1" DataContext="{Binding KeyboardViewModel}" />
    <local:ButtonsView Grid.Row="2" DataContext="{Binding ButtonsViewModel}" />
</Grid>

<UserControl x:Class="StudentScoreWpfProj.ButtonsView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:StudentScoreWpfProj"
         mc:Ignorable="d" 
         d:DataContext="{d:DesignInstance Type=local:ButtonsViewModel,IsDesignTimeCreatable=True}"
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <StackPanel Orientation="Horizontal">
        <Button Content="GoBack" Command="{Binding OnGoBackButton}"></Button>
        <Button Content="Continue"></Button>
    </StackPanel>
</Grid>

<UserControl x:Class="StudentScoreWpfProj.KeyboardView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:StudentScoreWpfProj"
         mc:Ignorable="d" 
         d:DataContext="{d:DesignInstance Type=local:KeyboardViewModel,IsDesignTimeCreatable=True}"
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
     <StackPanel Orientation="Horizontal">
        <Button Content="A" Command="{Binding OnClickButton}" CommandParameter="A"></Button>
        <Button Content="B" Command="{Binding OnClickButton}" CommandParameter="B"></Button>
        <Button Content="C" Command="{Binding OnClickButton}" CommandParameter="C"></Button>
    </StackPanel>
</Grid>

<UserControl x:Class="StudentScoreWpfProj.MessgaeView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:StudentScoreWpfProj"
         mc:Ignorable="d" 
         d:DataContext="{d:DesignInstance Type=local:MessageViewModel,IsDesignTimeCreatable=True}"
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <TextBox Text="{Binding Message}"/>
</Grid>

答案 1 :(得分:2)

你可以做几件事......

您可以创建一个静态实例以便于访问,并在其上公开您想要的内容(不推荐,阅读评论)。

您可以使用依赖注入,因此您的其他视图模型会将键盘视图模型作为参数(请查看my other answer,它会让您快速开始)。

您也可以使用信使来帮助您在他们之间进行交谈。大多数mvvm框架都会有一些(看看这个SO question,并在这个代码项目article来帮助你入门。它们专门用于MVVM灯,但它们可以帮助你理解概念)。

答案 2 :(得分:1)

如何使用Microsoft.Practices.ServiceLocation中的ServiceLocator?

ServiceLocator.Current.GetInstance<ViewModelName>();