【问题标题】:MVVM - Access xaml element in View from ViewModelMVVM - 从 ViewModel 访问视图中的 xaml 元素
【发布时间】:2018-05-07 15:44:36
【问题描述】:

型号:

  • TabModel.cs

  • DisplayTabViewModel.cs - 包含少数 TabModel 覆盖的属性,例如名称、标题等。

视图模型:

  • MainViewModel.cs

观看次数:

  • MainWindow.xaml - 窗口

  • DisplayTabView.xaml - 用户控件


这是纯 MVVM 模式,没有代码隐藏。 TabModel 在 Word 文档关闭后调用 EventHandler - RefreshRequestedMainViewModel 包含订阅者。 MainViewModel 中的方法 Refresh() 工作正常。该方法在我关闭文档后触发。现在我想从 MainViewModel 刷新 DisplayTabView.xaml 中的 WebBrowser。我已经为此苦苦挣扎了太多小时。你能否指出我正确的方向。谢谢。


TabModel.cs

public abstract class TabModel : ITabModel, INotifyPropertyChanged
{
    public async void HiddenFileExists()
    {
        (...)

        OnRefreshRequested();
    }

    public delegate void RefreshRequestedEventHandler(object source, EventArgs args);

    public event RefreshRequestedEventHandler RefreshRequested;

    public virtual void OnRefreshRequested()
    {
        RefreshRequested?.Invoke(this, EventArgs.Empty);
    }
}

MainViewModel.cs

public class MainViewModel : INotifyPropertyChanged
{
    private readonly ObservableCollection<ITabModel> tabs;

    private void DisplayFileTab(object parameter)
    {
        (...)

        tabs.ElementAt(TabIndex).RefreshRequested += Refresh;
    }

    public void Refresh(object source, EventArgs args)
    {
        MessageBox.Show("Refresh");
    }

DisplayTabView.xaml

<Grid>
    <WebBrowser h:WebBrowserExtensions.BindableSource="{Binding FileUrl}" Tag="{Binding AcceptedKeywordsArray}" h:WebBrowserExtensions.BindableLoaded="{Binding}" />
</Grid>

MainWindow.xaml

<Grid>
    <DockPanel>
        <TabControl Name="tabControl" ItemsSource="{Binding Tabs}" SelectedIndex="{Binding TabIndex}">
            <TabControl.Resources>
                <DataTemplate DataType="{x:Type m:DisplayTabViewModel}">
                    <v:DisplayTabView x:Name="DisplayTab"/>
                </DataTemplate>
            </TabControl.Resources>
            <TabControl.ItemTemplate>
                <DataTemplate DataType="{x:Type m:ITabModel}">
                    <Grid>
                        (...)
                    </Grid>
                </DataTemplate>
            </TabControl.ItemTemplate>
        </TabControl>
    </DockPanel>
</Grid>

【问题讨论】:

  • MVVM != 没有代码隐藏。您的 UI 逻辑在代码隐藏中很好。从视图模型访问 UI 元素!= MVVM。
  • 建议:不要使用 ViewModel 在 UI 上假设或执行操作。假设您不知道 UI 对您提供的信息做了什么。在 UI 上,为了防止代码落后(我喜欢!),您只需要倾听您的数据并从中工作。强制从 ViewModel 刷新是不好的,从 Model 更糟。那不是 MVVM 并且远离纯 MVVM。如果不完全了解用例,我认为您不会得到您正在寻找的正确答案。如果您逐步说明您要实现的功能,我可以提供帮助。

标签: c# wpf xaml mvvm


【解决方案1】:

MainViewModel 可以使用事件聚合器或信使发送事件/消息,您的应用程序中的任何其他组件(例如另一个视图模型或视图)都可以订阅该事件/消息。

通过在发布者和订阅者之间引入事件聚合器或信使,您可以消除事件发布者和消费者之间的紧密耦合。发布者和消费者都只知道事件聚合器,但他们对彼此一无所知,也没有任何引用。有关该概念的更多信息,请参阅以下链接。

使用事件聚合器模式在视图模型之间进行通信: https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/

MVVM - MVVM 中的 Messenger 和查看服务: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx

大多数 MVVM 库都有自己的此类实现。在Prism 中,它被称为EventAggreator。在MvvmLight 中,它被称为Messenger

以下是一些关于如何使用它们的代码示例:

https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/14-UsingEventAggregator

https://marcominerva.wordpress.com/2014/06/25/how-to-send-string-and-content-messages-with-mvvm-light-messenger/

如果您愿意,当然也可以实现自己的自定义事件聚合器。

【讨论】:

    【解决方案2】:

    几乎所有 MVVM 框架都实现了一个消息总线,您可以使用它在视图模型之间发送消息,在您的情况下,从 MainViewModel 到 DisplayTabViewModel。

    如果您出于某种原因反对使用 MVVM 框架,您将不得不实现自己的消息总线,这没有多大意义,因为那里有很多很棒的 MVVM 框架......

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 2010-12-01
    相关资源
    最近更新 更多