【问题标题】:MVVM - UserControl Loaded only first timeMVVM - UserControl 仅第一次加载
【发布时间】:2020-09-11 21:18:11
【问题描述】:

我正在使用 ViewModel-first aprroach。在 MainWindow.xaml 中,我设置了 ContentControl,通过单击 MenuItem 来显示我的 UserControls(视图)。当我第一次点击显示 UserControl 时,一切正常。

但是当我单击相同的 MenuItem 再次打开它时,我的 UserControl 再次显示但不再加载,导致没有刷新绑定。将我的 ContentControl 的 Content 设置为 null 并不能解决问题。

我的整个设置是这样的:

1.) App.xaml 资源

 <!--DataContext for MainWindow.xaml-->
 <ViewModels:MainWindowViewModel x:Key="Main_VM"/>

 <!--DataTemplate for UserControl-->
 <DataTemplate DataType="{x:Type ViewModels:MyViewModel}">
    <Views:MyView />
 </DataTemplate>

2.) MainWindow.xaml,我的 ContenControl 所在的位置

     <Window x:Class="My.Views.MainWindowView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            DataContext="{StaticResource Main_VM}">
      <Grid>
    
       <!--Menu which opens view on command-->
        <Menu VerticalAlignment="Top" IsMainMenu="True" >
          <MenuItem Header="My View" Command="{Binding Show_View}" CommandParameter="1"/>
        </Menu>

       <ContentControl Content="{Binding Display_View}" />
  
       <!--And all other controls, like Menu for opening views on click...-->

      </Grid>
     </Window>

3.) Mainwindow.xaml 的 ViewModel(继承自 BaseViewModel)

  public class MainWindowViewModel : BaseViewModel
  {
        public MainWindowViewModel()
        {
           //Command for displaying Views
            Show_View = new Relay_Command(Open_view, null);
        }
           
        public ICommand Show_View { get; set; }
    
        private BaseViewModel _display_view;
        public BaseViewModel Display_View
        {
            get { return _display_view; }
            set { _display_view = value; OnPropertyChanged(); }
        }
     
        private void Open_view(object parameter)
        {
           
            Display_View = null; //This doesn't help at all!!!

            switch (parameter)
            {
                case "1": 
                 Display_View= new MyViewModel();
                 break;
            }
        }
    }
 

4.) 还有我的 UserControl.xaml

<UserControl x:Class="MyProject.Views.MyView"
             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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
             mc:Ignorable="d" 
             d:DesignHeight="450" 
             d:DesignWidth="800">

    <!--Event-->
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
           
            <!--Calling a method on Load (firing only first tme !!)-->
            <ei:CallMethodAction MethodName="MethodForRetrievingData" TargetObject="{Binding}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Grid>

      <!--Controls in UserControl for binding etc...-->

    </Grid>
</UserControl>

我尝试过调试,但如告知的那样,UserControl 的加载事件只发生一次。我对此一无所知,看来我的设计有缺陷。

这可能是什么问题,也许我在 UserControl 本身上缺少 NotifyProperty 之类的东西?

【问题讨论】:

  • 你能把Display_View绑定到content control的代码贴出来吗?根据您当前发布的代码`` 我看到 Show_View 有一个绑定
  • @user1672994,感谢您注意到,这是一个错字,很抱歉 - Display_View 是绑定到 ContentControl 的。我在 ManWindow.xaml 中编辑了问题,我添加了一个菜单控件,它也可以打开视图 - 它绑定到 Show_View

标签: c# wpf xaml mvvm


【解决方案1】:

您需要实际卸载视图才能再次加载它。在将ContentControlContent 属性的源属性设置为null 之前将其设置为另一个MyViewModel 不会卸载视图。 DataTemplate 被“缓存”了。

为什么不从视图模型本身调用MethodForRetrievingData,而不是依靠视图引发Loaded 事件?例如,您可以initialize it asynchronously

【讨论】:

  • 这正是我想出来的,aDisplayName 指出了我。我已经将代码更改为从视图模型调用 MethodForRetrievingData。但是 - 如果您等待的时间足够长 - 大约一两秒 - 当您将 ContentControl 设置为 null 时,View get's Loaded 再次。奇怪的是,每次都在另一边卸货....
【解决方案2】:

不要使用模板显示 MyViewModel 对象内容,而是尝试显示 MyView 内容。

所以你会有


        private MyView _display_view;
        public MyView Display_View
        {
            get { return _display_view; }
            set { _display_view = value; OnPropertyChanged(); }
        }

        private void Open_view(object parameter)
        {
           
            Display_View = null; //This doesn't help at all!!!

            switch (parameter)
            {
                case "1": 
                 Display_View= new MyView(); // Or assign a view model here: {DataContext=new MyViewModel()}
                 break;
            }
        }

【讨论】:

  • 我不应该在 viewModel 中引用 View,这违反了 MVVM。
  • 之所以说“不应该”而不是“不应该”是因为有些情况下你仍然需要这样做。您在 MyView 中调用“MethodForRetrievingData”。看起来它与数据相关,那么您违反了自己的不混合数据和视图的规则。要么将“MethodForRetrievingData”移动到 MyViewModel,要么将整个视图视为数据。
  • 这不是Interactivity dll的一点吗?要在 MVVM 中像这样使用它 - 在 ViewModel 中有代码但将它与 .xaml 中的 View 事件连接?我现在很困惑。将 MethodForRetrievingData 移动到 Viewmodel 构造函数不会导致与 UserControl 的 Load 事件相同,UI 的呈现会变得不同。这不是我想要的。
  • 除此之外,我认为这不是问题。问题是用户控件没有第二次加载,尽管我可以看到它显示。所以将代码移动到任何地方对我没有帮助 - 因为 Load 事件不再触发,所以代码不会被执行。
  • 我没有关于数据模板如何在后台工作的源代码。我的猜测是,MyView 是模板的一部分,它只在由“ContentControl”创建时启动一次,即使Display_Viewnull。您可以在 1) MethodForRetrievingData, 2) Open_View 中设置断点。我希望MEthodForRetrievingData 中的断点可以在“Open_VIew”之前命中。我可能错了。
猜你喜欢
  • 2021-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-25
相关资源
最近更新 更多