c# - 将 ViewModel 引用传递给另一个 ViewModel(绑定(bind)到 View 的同一引用)?

标签 c# wpf mvvm

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

keyboard and buttons

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

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

我可以创建一个变量:

private KeyboardViewModel keyboard;

但是我如何使用主视图已有的实例实例化该变量(以便我可以从按钮 View 模型访问这些属性)?

最佳答案

主要问题是,当实际上需要在多个 View/ViewModel 中重用数据源时,您将数据源错放在其中一个 ViewModel 中。您需要做的是将数据源重构为单例实例或可以注入(inject)不同 ViewModels 构造函数的单独实例。通过将数据源与特定的 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>

关于c# - 将 ViewModel 引用传递给另一个 ViewModel(绑定(bind)到 View 的同一引用)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32595480/

相关文章:

c# - 对于这种使用 BitmapSource 不断更新相机图像的情况,如何避免显式调用 GC.Collect()

c# - 如何在WPF中获取应用程序名称

wpf - 如何使用 mvvm 在 wpf 中制作可编辑的数据网格?

c# - Dapper - 从字符串到 int 的 Sql 到对象隐式转换

c# - 如何反编译/编译dll文件?

c# - 代码隐藏中的动态上下文菜单

java - 这是将 MVVM 结构与 ViewModel 和 RecyclerView 一起使用而没有任何问题的正确方法吗?

c# - 从 AvalonDock 2.0 中的集合绑定(bind) LayoutDocument 的标题

c# - ILogger 不遵守 Application Insights 的日志级别

c# - 使用 RemoveChild() 删除子节点