2013-03-14 173 views
2

我對MVVM非常陌生,並且現在完成了我的第一個POC。但是,我一直在努力解決2天內的問題。認爲向你們解釋可能會很快幫助並解決問題。現在讓我簡要介紹我的問題。 我有一個主視圖綁定到MainViewModel的WPF MVVM應用程序。我在這裏有Textblock來綁定視圖模型中的一些內容,同時加載正在工作的屏幕。 我也有ChildUserControl綁定到ChildViewModel。現在,我需要將不同的內容綁定到主窗口中的Textblock,從用戶控制用戶控制級別發生的某些操作。怎麼可能?MVVM主窗口控件綁定從子用戶控件

這裏是示例代碼我 MainWindow.Xaml

<Window.Resources> 
    <viewModel:MainViewModel x:Key="mainWindowViewModel"/></Window.Resources> 

<TextBlock Name="txtStatus" Text="{Binding StatusMessage, Mode=OneWay }"/> 

ChildUserControl.xaml

<UserControl.Resources> 
    <viewModel:ChildModelView x:Key="ChildModelView"/> </UserControl.Resources> 

public class ChildModelView : BaseViewModel 
{ 
// Some child level logic.. 
// then need to update the txtStatus text block from parent 
} 

你的幫助是非常感謝..!

回答

4

實現此目的的簡單方法是使用IoC。當您創建子視圖模型時,將主視圖模型的引用傳遞給您的子視圖模型,並將其保存爲私有隻讀變量。您現在訪問所有主要VM公衆。

另一種解決方案可能是使用介體模式。

敲了一個簡單的應用程序來演示IoC解決方案。

App.xaml.cs

public partial class App : Application 
{ 
    protected override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     var window = new MainWindow() {DataContext = new MainWindowViewModel() }; 
     window.Show(); 
    } 
} 

MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase 
{ 
    private string _statusMessage; 

    public string StatusMessage 
    { 
     get { return _statusMessage; } 
     set { _statusMessage = value; this.OnPropertyChanged("StatusMessage"); } 
    } 

    public ICommand OpenChildCommand { get; private set; } 

    public MainWindowViewModel() 
    { 
     this.StatusMessage = "No status"; 
     this.OpenChildCommand = new DelegateCommand((o) => this.OpenChild()); 
    } 

    private void OpenChild() 
    { 
     var window = new ChildWindow {DataContext = new ChildWindowViewModel(this)}; 
     window.Show(); 
    } 
} 

ChildWindowViewModel.cs

public class ChildWindowViewModel : ViewModelBase 
{ 
    private readonly MainWindowViewModel _mainvm; 

    public ChildWindowViewModel(MainWindowViewModel mainvm) 
    { 
     _mainvm = mainvm; 
     this.UpdateStatusCommand = new DelegateCommand((o) => this.UpdateStatus()); 
    } 

    public ICommand UpdateStatusCommand { get; private set; } 

    private void UpdateStatus() 
    { 
     this._mainvm.StatusMessage = "New Status"; 
    } 
} 

ViewModelBase.cs

public abstract class ViewModelBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string propertyName) 
    { 
     this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 
    } 

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
     var handler = this.PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, e); 
     } 
    } 
} 

DelegateCommand.cs

public class DelegateCommand : ICommand 
{ 
    /// <summary> 
    /// Action to be performed when this command is executed 
    /// </summary> 
    private Action<object> executionAction; 

    /// <summary> 
    /// Predicate to determine if the command is valid for execution 
    /// </summary> 
    private Predicate<object> canExecutePredicate; 

    /// <summary> 
    /// Initializes a new instance of the DelegateCommand class. 
    /// The command will always be valid for execution. 
    /// </summary> 
    /// <param name="execute">The delegate to call on execution</param> 
    public DelegateCommand(Action<object> execute) 
     : this(execute, null) 
    { 
    } 

    /// <summary> 
    /// Initializes a new instance of the DelegateCommand class. 
    /// </summary> 
    /// <param name="execute">The delegate to call on execution</param> 
    /// <param name="canExecute">The predicate to determine if command is valid for execution</param> 
    public DelegateCommand(Action<object> execute, Predicate<object> canExecute) 
    { 
     if (execute == null) 
     { 
      throw new ArgumentNullException("execute"); 
     } 

     this.executionAction = execute; 
     this.canExecutePredicate = canExecute; 
    } 

    /// <summary> 
    /// Raised when CanExecute is changed 
    /// </summary> 
    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    /// <summary> 
    /// Executes the delegate backing this DelegateCommand 
    /// </summary> 
    /// <param name="parameter">parameter to pass to predicate</param> 
    /// <returns>True if command is valid for execution</returns> 
    public bool CanExecute(object parameter) 
    { 
     return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter); 
    } 

    /// <summary> 
    /// Executes the delegate backing this DelegateCommand 
    /// </summary> 
    /// <param name="parameter">parameter to pass to delegate</param> 
    /// <exception cref="InvalidOperationException">Thrown if CanExecute returns false</exception> 
    public void Execute(object parameter) 
    { 
     if (!this.CanExecute(parameter)) 
     { 
      throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute."); 
     } 
     this.executionAction(parameter); 
    } 
} 

MainWindow.xaml

<Window x:Class="WpfApplication2.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
<StackPanel> 
    <TextBlock Text="{Binding StatusMessage, Mode=OneWay}" /> 
    <Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="Open Child Window" 
      Command="{Binding Path=OpenChildCommand}"/> 
</StackPanel> 

ChildWindow。XAML

<Window x:Class="WpfApplication2.ChildWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="ChildWindow" Height="300" Width="300"> 
<Grid> 
    <Button HorizontalAlignment="Center" VerticalAlignment="Center" Content="UpdateStatus" 
      Command="{Binding Path=UpdateStatusCommand}" /> 
</Grid> 

點擊更新狀態

Before

圖片點擊updatestatus

enter image description here

+0

我在啓動ChildVM時傳遞了主視圖模型,並將MainVM屬性設置了一些值。但它確實也反映了整個頁面......不知道爲什麼。 – user2066540 2013-03-14 21:03:23

+0

您是否在mainvm上實現INPC並在mainvm屬性上調用propertychanged? – failedprogramming 2013-03-14 21:07:47

+0

這是我即將在我的MainMV公共字符串中執行的狀態消息 { get {return statusMsg; } set { statusMsg = value; OnPropertyChanged(「StatusMessage」); } } 然後,當初始化子vm im做ChildVM(mainVM vm){_main = vm; _main.StatusMessage =「新狀態」;這是正確的方式嗎? – user2066540 2013-03-14 21:13:16

0

我可能會誤解你的需要,他前後圖片但是它聽起來像是在MainWindow中有一個TextBlock,它需要更新以響應由ChildWindow提供的數據中出現的內容。假設這就是你想要做的,那麼我認爲有幾種不同的方法可以做到這一點。特別是,您可以使用附加事件。

http://msdn.microsoft.com/en-us/library/bb613550.aspx

的技術解釋是有點長,所以我要把它留給MSDN要做到這一點,但短期的解釋很簡單,它只是將你的子窗口被提出和處理的事件通過你的MainWindow。這裏的好處是,它將作爲DataContext傳入你的ChildWindowViewModel。