MVVM C#在视图之间传递数据(窗口)

时间:2016-05-05 09:06:18

标签: c# wpf mvvm

1)在多个视图之间传递数据的最佳方法是什么?

2)我有场景(MVVM C#):

MainWindow中的TextBox和Button以及Window1中的TextBlock, 在按钮上单击(我正在使用Icommand)MainWindow的TextBox中的数据必须出现在Window1的TextBlock中?

ViewModelBase.cs

public class ViewModelBase
{
    public Commandclass commandclass { get; set; }

    public ViewModelBase()
    {
        commandclass = new Commandclass(this);
    }

    private string fname;        
    public string vmname
    {
        get { return fname; }
        set { fname = value; }
    }        

    public void OnCommand()
    {         
        Window1 w = new Window1();
        /* How to bind ???*/
        w.Show();
    }
}

CommandClass.cs

public class Commandclass : ICommand
    {
        public ViewModelBase vmclass { get; set; }
        public Commandclass(ViewModelBase vmb)
        {
            vmclass = vmb;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            vmclass.OnCommand();
        }
    }

浏览

**MainWindow.xaml**

<Window x:Class="Multiwindow.MainWindow"
        …
        xmlns:vm="clr-namespace:Multiwindow.Viewmodel">
    <Window.Resources>        
        <vm:ViewModelBase x:Key="vmodel"/>
    </Window.Resources>

    <Grid Background="Gray" DataContext="{StaticResource vmodel}">

        <TextBox Height="26" Margin="194,115,154,179" Width="169" 
                  Text="{Binding vmname, Mode=TwoWay}"/>
        <Button Content="Button1" HorizontalAlignment="Left" 
                Margin="251,158,0,0" VerticalAlignment="Top"
                Command="{Binding commandclass, Source={StaticResource vmodel}}"/>

    </Grid>
</Window>

**Window1.xaml**

<Window.Resources>
        <vm:ViewModelBase x:Key="vmodel"/>
</Window.Resources>
<Grid >
    <TextBlock FontSize="20" Height="28" Width="169" Foreground="Black"
                    Background="Bisque" />
</Grid>

我用谷歌搜索并发现了一个项目,但它是复杂的,请建议我的答案2)问题将有所帮助..谢谢。

4 个答案:

答案 0 :(得分:4)

我就是这样做的。在命令按钮上单击按钮,执行以下操作:

Window2 w= new Window2();
w.DataContext=new Window2ViewModel();
((Window2ViewModel)w.DataContext).TextForTextblock=TextFromTextbox;
w.Show();

修改

看到您的代码,您可以这样做,因为我认为两个窗口共享ViewModelBase:

Window1 w= new Window1();
w.DataContext=this;
w.Show();

您还必须绑定TextBlock:

<TextBlock FontSize="20" Height="28" Width="169" Foreground="Black"
                Background="Bisque" Text="{Binding vmname}"/>

答案 1 :(得分:1)

我假设您打算在MainWindow和Window1之间共享ViewModelBase。在这种情况下,您无法将ViewModelBase实例添加到MainWindow.Resources,然后将另一个实例添加到Window1.Resources。 如果您没有解释原因,请查看一些C#OOP教程。

要在多个视图之间共享ViewModelBase的同一实例,您必须只创建一个资源。可以从所有视图访问Application.Resource。

  1. 将ViewModelBase添加到app.xaml中的Application.Resources
  2. 从MainWindow.Resources和Window1.Resources中删除ViewModelBase
  3. 就是这样。

    但是,建议您为每个View提供单独的ViewModel类。

    在这种情况下,你可能会有这样的事情:

    <Window x:Class="Multiwindow.MainWindow"
            xmlns:vm="clr-namespace:Multiwindow.Viewmodel">
        <Window.DataContext>        
            <vm:MainWindowViewModel />
        </Window.DataContext>
        ...
    </Window>
    
    **Window1.xaml**
    
    <Window.DataContext>
        <vm:Window1ViewModel />
    </Window.Resources>
    

    除了@ Pikoh的解决方案,我建议如下:

    1. 构造函数参数

      var window1 = new Window1("hello world");
      windows1.Show();
      
      public class Window1(string parameter){...
      
    2. 我在视图上设置了 ViewModel属性,并允许在视图上创建ViemModel的重复性。

      var window1 = new Window1();
      window1.ViewModel.Parameter = "Hello world";
      
      public class Window1{
         ...
         public Window1ViewModel { get {return (Window1ViewModel)DataContext;}}
      }
      

      datacontext应该在ctor或XAML中设置。

    3. ViewModel首次方法:

      a)为第二个窗口创建视图模型

      b)在viewmodel上设置参数

      c)使用自定义DialogService类根据使用命名约定的viewmodel类型为您创建视图并显示它。

      这样你甚至不会触摸视图模型中的任何视图,并且你的视图模型与视图完全分开,因此它很容易测试。运行单元测试时,可以轻松替换DialogService实现。

      //in MainWindowViewModel:
      
      var secondWindowViewModel = new SecondWindowViewModel();
      //alternativelly:
      //secondWindowViewModel = ViewModelLocator.Resolve<SecondWindowViewModel>();
      secondWindowViewModel.Parameter = "Hello world";
      dialogService.Show(secondWindowViewModel); 
      

答案 2 :(得分:0)

如果要在视图模型之间共享数据,则需要将中介实现应用于您的代码。

请查看here您会找到有关它的更多信息。

答案 3 :(得分:0)

以下是您的问题的完整答案:

主窗口xaml代码

<Window x:Class="TwoWindowsDialogSoHelpAttempt.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:twoWindowsDialogSoHelpAttempt="clr-namespace:TwoWindowsDialogSoHelpAttempt"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <!--Main window data context declaration-->
    <twoWindowsDialogSoHelpAttempt:FirstWindowDataContext/>
</Window.DataContext>
<i:Interaction.Behaviors>
    <!--next behavior helps to control loading of the second window-->
    <twoWindowsDialogSoHelpAttempt:SecondWindowLoadingBehavior 
        IsSecondWindowShouldBeShown="{Binding IsSecondWindowShouldBeShown, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
        CanProvideDataForSecondWindow="{Binding CanProvideDataForSecondWindow, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors>
<Grid>
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Border Width="150" Height="30" BorderBrush="Tomato" BorderThickness="1">
            <TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"></TextBox>
        </Border>
        <Button Command="{Binding Command, UpdateSourceTrigger=PropertyChanged}">Show second Window</Button>
    </StackPanel>
</Grid>

第二个窗口xaml代码

<Window x:Class="TwoWindowsDialogSoHelpAttempt.SecondWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="SecondWindow" Height="300" Width="300">
<Grid Background="Green">
    <TextBlock Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="150"></TextBlock>
</Grid>

第二个窗口加载行为,有助于控制SecondWindow外观,并将ICanProvideDataForSecondWindow注入到第二个窗口视图模型中

public class SecondWindowLoadingBehavior:Behavior<FrameworkElement>
{
    private Window _window;


    public static readonly DependencyProperty CanProvideDataForSecondWindowProperty = DependencyProperty.Register(
        "CanProvideDataForSecondWindow", typeof (ICanProvideDataForSecondWindow), typeof (SecondWindowLoadingBehavior), new PropertyMetadata(default(ICanProvideDataForSecondWindow)));

    /// <summary>
    /// helps to control dialog between first and second window
    /// </summary>
    public ICanProvideDataForSecondWindow CanProvideDataForSecondWindow
    {
        get { return (ICanProvideDataForSecondWindow) GetValue(CanProvideDataForSecondWindowProperty); }
        set { SetValue(CanProvideDataForSecondWindowProperty, value); }
    }

    public static readonly DependencyProperty IsSecondWindowShouldBeShownProperty = DependencyProperty.Register(
        "IsSecondWindowShouldBeShown", typeof (bool), typeof (SecondWindowLoadingBehavior), new PropertyMetadata(default(bool), IsSecondWindowShouldBeShownPropertyChangedCallback));

    //when the IsSecondWindowShouldBeShown dependency property will be changed, will trigger the window showing
    private static void IsSecondWindowShouldBeShownPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        var isShouldBeShown = (bool) args.NewValue;
        var behavior = dependencyObject as SecondWindowLoadingBehavior;
        if (isShouldBeShown == false || behavior == null || behavior.CanProvideDataForSecondWindow == null)
        {
            return;
        }
        behavior.ShowSecondWindow();
    }

    /// <summary>
    /// helps to control the second window loading
    /// </summary>
    public bool IsSecondWindowShouldBeShown
    {
        get { return (bool)GetValue(IsSecondWindowShouldBeShownProperty); }
        set { SetValue(IsSecondWindowShouldBeShownProperty, value); }
    }

    //helps to prepare and show the second window
    private void ShowSecondWindow()
    {
        _window = new SecondWindow {DataContext = new SecondWindowDataContext(CanProvideDataForSecondWindow)};
        _window.Closing += WindowOnClosing;
        _window.Show();
    }

    //disposes a data context instance when a window was closed
    private void WindowOnClosing(object sender, CancelEventArgs e)
    {
        _window.Closing -= WindowOnClosing;
        IsSecondWindowShouldBeShown = false;
        var disposableDataContext = _window.DataContext as IDisposable;
        if (disposableDataContext == null) return;
        disposableDataContext.Dispose();
    }
}

查看模型和帮助

主窗口视图模型,实现ICanProvideDataForSecondWindow以便能够动态发送数据并更改第二个窗口内容

 /// <summary>
/// a first window data context, provides data to the second window based on a
///  ICanProvideDataForSecondWindow implementation
/// </summary>
public class FirstWindowDataContext:BaseObservableObject, ICanProvideDataForSecondWindow
{
    private string _text;
    private ICommand _command;
    private bool _isSecondWindowShouldBeShown;
    private ICanProvideDataForSecondWindow _canProvideDataForSecondWindow;

    public FirstWindowDataContext()
    {
        CanProvideDataForSecondWindow = this;
    }

    public ICanProvideDataForSecondWindow CanProvideDataForSecondWindow
    {
        get { return _canProvideDataForSecondWindow; }
        set
        {
            _canProvideDataForSecondWindow = value;
            OnPropertyChanged();
        }
    }

    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            OnPropertyChanged();
            OnSecondWindowDataEventHandler(new DataForSecondWindowArgs{Data = Text});
        }
    }

    public ICommand Command
    {
        get { return _command ?? (_command = new RelayCommand(ShowSecondWindow)); }
    }

    //method to show the second window
    private void ShowSecondWindow()
    {
        //will show the window if it isn't opened yet
        if(IsSecondWindowShouldBeShown) return;
        IsSecondWindowShouldBeShown = true;
    }

    public bool IsSecondWindowShouldBeShown
    {
        get { return _isSecondWindowShouldBeShown; }
        set
        {
            _isSecondWindowShouldBeShown = value;
            OnPropertyChanged();
        }
    }

    public event EventHandler<DataForSecondWindowArgs> SecondWindowDataEventHandler;

    protected virtual void OnSecondWindowDataEventHandler(DataForSecondWindowArgs e)
    {
        var handler = SecondWindowDataEventHandler;
        if (handler != null) handler(this, e);
    }
}

ICanProvideDataForSecondWindow - 帮助主要和第二个窗口视图模型之间进行通信的界面

public interface ICanProvideDataForSecondWindow
{
    event EventHandler<DataForSecondWindowArgs> SecondWindowDataEventHandler;
    string Text { get; set; }
}

DataForSecondWindowArgs - 在视图模型通信期间传递的参数

public class DataForSecondWindowArgs:EventArgs
{
    public string Data { get; set; }
}

SecondWindow数据上下文 - 是侦听器,通过ICanProvideDataForSecondWindow接口获取数据。

 /// <summary>
/// second window data context, listening for the changes in first window data context to show them on the second window
/// </summary>
class SecondWindowDataContext:DisposableBaseObservableObject
{
    private readonly ICanProvideDataForSecondWindow _canProvideDataForSecondWindow;
    private string _text;

    public SecondWindowDataContext(ICanProvideDataForSecondWindow canProvideDataForSecondWindow)
    {
        _canProvideDataForSecondWindow = canProvideDataForSecondWindow;
        _canProvideDataForSecondWindow.SecondWindowDataEventHandler += CanProvideDataForSecondWindowOnSecondWindowDataEventHandler;
        Text = _canProvideDataForSecondWindow.Text;
    }

    private void CanProvideDataForSecondWindowOnSecondWindowDataEventHandler(object sender, DataForSecondWindowArgs args)
    {
        Text = args.Data;
    }

    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            OnPropertyChanged();
        }
    }

    protected override void DisposeOverride()
    {
        base.DisposeOverride();
        _canProvideDataForSecondWindow.SecondWindowDataEventHandler -= CanProvideDataForSecondWindowOnSecondWindowDataEventHandler;
    }
}

INPC实现和命令(您可以将其替换为您自己的Commandclass)代码

 /// <summary>
/// implements the INotifyPropertyChanged (.net 4.5)
/// </summary>
public class DisposableBaseObservableObject : INotifyPropertyChanged, IDisposable
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
    {
        var propName = ((MemberExpression)raiser.Body).Member.Name;
        OnPropertyChanged(propName);
    }

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(name);
            return true;
        }
        return false;
    }

    #region Disposable

    private bool _isDisposed;
    ~DisposableBaseObservableObject()
    {
        Dispose();
    }

    public void Dispose()
    {
        if (_isDisposed)
        {
            return;
        }
        GC.SuppressFinalize(this);
        _isDisposed = true;
        DisposeOverride();
    }

    protected virtual void DisposeOverride()
    {

    }

    #endregion
}

public class RelayCommand : ICommand
{
    private readonly Func<bool> _canExecute;
    private readonly Action _execute;

    public RelayCommand(Action execute)
        : this(() => true, execute)
    {
    }

    public RelayCommand(Func<bool> canExecute, Action execute)
    {
        _canExecute = canExecute;
        _execute = execute;
    }

    public bool CanExecute(object parameter = null)
    {
        return _canExecute();
    }

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

    public event EventHandler CanExecuteChanged;
}

<强>说明

  1. MainWindow - 就像你的第一个窗口。
  2. SecondWindow - 就像你的第二个窗口。
  3. SecondWindowLoadingBehavior - 行为(链接 - what is behavior and how to create behaviorwhere is behavior dll)用于控制第二个窗口外观。
  4. FirstWindowDataContext - 主窗口数据上下文,是第二个窗口的数据提供者。
  5. ICanProvideDataForSecondWindow - 为第二个窗口提供数据的界面(适用于dependency injectionSOLID)。
  6. SecondWindowDataContext - 第二个窗口数据上下文,侦听主窗口数据上下文中的更改(这里ICanProvideDataForSecondWindow注入它的c'tor)。
  7. 此外,您还可以看到一些基于IEventAggregator的解决方案(EventAggregator example)。
  8. 如果需要,我很乐意帮助提供其他解释。 请告诉我。

    问候。