簡體   English   中英

從UserControl訪問窗口ViewModel

[英]Access Window ViewModel from UserControl

我有一個帶有ContentControl的窗口。 我想顯示多個視圖填充用戶控件,如具有多個步驟的向導。 這些UserControl需要它們自己的ViewModel,並有可能用Window的ContentControl中的另一個UserControl替換它們。

我想使用MVVM模式,目前正在努力如何從UserControl的ViewModel訪問窗口的ViewModel。

這是到目前為止的簡化代碼。 當我在主ViewModel中更改內容時,內容更改可以正常工作:

視窗XAML:

<Grid>
    <ContentControl Content="{Binding CurrentView}" />
</Grid>

窗口ViewModel:

public class MainWindowViewModel : ViewModelBase
{
    private object currentView;

    public object CurrentView
    {
        get { return currentView; }
        private set
        {
            currentView = value;
            OnPropertyChanged(); // <- Property name is set automatically, so no parameter needed
        }
    }

    public MainWindowViewModel()
    {
        this.CurrentView = new UserControl1(); // Initial view to show within the ContentControl
    }
}

UserControl1 XAML:

<UserControl>
    <Grid>
        <Button Command="{Binding SwitchToUserControl2}">Switch content</Button>
    </Grid>
</UserControl>

現在,我有以下“思考問題”:

  • 如果將UserControl的DataContext設置為其ViewModel,則無法訪問MainWindowViewModel將CurrentView屬性更改為UserControl2。
  • 如果未設置UserControl的DataContext,我將自動繼承正確的ViewModel以綁定命令以更改內容,但尚未實例化UserControl的ViewModel。 我需要這樣做是因為UserControl的許多操作都應在其自己的ViewModel中處理。

以我的理解,有必要從視圖訪問兩個ViewModel,但是不知道如何實現。

我不會讓MainWindowViewModel創建一個視圖 ,而是創建您的第一個ViewModel。 然后,ViewModel可以使用事件或任何其他機制來通知它應過渡到下一步。

在這種情況下,可以通過將ViewModel映射到適當的View的DataTemplates輕松處理View部分。 這樣做的好處是ViewModel永遠不知道用於渲染它的View,它在MVVM透視圖中保持“純凈”狀態。 現在,您的ViewModel正在操縱View層,這是MVVM違規。

Reed的回答是正確的,並且是解決問題的一種方法,可以在MainWindow中創建控件的ViewModel,連接事件,然后通過DependencyProperty將ViewModel綁定到用戶控件。

若要使ViewModel綁定起作用,請確保未在UserControl的構造函數中或UserControl Xaml的Root元素上未設置DataContext。 而是在UserControl的第一個內容元素上設置DataContext。 這將允許與UserControl的外部綁定繼續運行,而所需的是UserControl的DataContext。

<UserControl x:Class="StackOverflow._20914503.UserControl1" 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:this="clr-namespace:StackOverflow._20914503"
             mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    <Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:UserControl1}}, Path=ViewModel}">
    </Grid>
</UserControl>

關於換入和換出控件,Reed還是正確的,DataTemplates是必經之路。

解決通信問題的另一種方法是使用RoutedEvents 在應用程序中創建一個RoutedEvent,由於該事件與ui元素沒有真正的關聯,因此讓我們創建一個類來發布路由事件。

public static class EventManagement
{
    public static readonly RoutedEvent ChangeViewEvent = EventManager.RegisterRoutedEvent("ChangeView", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(UserControl));
}

現在,在每個UserControls中(必須在UserControl后面的代碼中完成),您可以調用RaiseEvent,該事件在UIElement類中實現。 在下面的代碼中,我從UserControl的ViewModel中拾取一個事件,並觸發RoutedEvent

    private void ViewModel_ChangeEvent(object sender, EventArgs e)
    {
        RaiseEvent(new RoutedEventArgs(EventManagement.ChangeViewEvent));
    }

在我的主窗口中,不知道將從何處觸發RoutedEvent,我可以像這樣將處理程序添加到Routed事件中

    public MainWindow()
    {
        InitializeComponent();
        this.AddHandler(EventManagement.ChangeViewEvent, new RoutedEventHandler(SomeControl_ChangeView));
    }

    private void SomeControl_ChangeView(object sender, RoutedEventArgs routedEventArgs)
    {
    }

.Net將根據RoutedEvent注冊為您處理事件的路由。

這種方法的優點是功能分離。 一切正常,無需任何其他操作。 您可以使用觸發器將UserControl插入到MainWindow中,它們都可以引發相同的RoutedEvent,並且MainWindow將處理它們。

總結控制流。 UserControl的ViewModel引發UserControl處理的標准CLR事件。 UserControl引發RoutedEvent。 .Net將事件冒泡到主窗口。 主窗口通過其處理程序接收事件。

需要注意的幾點。 1. RoutedEvents的默認路由策略是“冒泡”(從最低元素(例如按鈕)到最高元素(例如MainWindow))。 1.一旦處理程序將事件標記為“已處理”,事件將停止。 1.路由主要通過可視樹完成

如有必要,我可以發布示例的組成部分。

我希望這有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM