【问题标题】:ContentControl not refreshing View when new ViewModel is created创建新 ViewModel 时,ContentControl 不刷新 View
【发布时间】:2019-09-14 21:46:24
【问题描述】:

我正在尝试使用 MVVMLight 制作一个应用程序,该应用程序有一个带有按钮的侧边栏,每个按钮显示一个托管在 ContentControl 中的新用户控件。当用户单击侧边栏中的按钮(显示 View1 / Show View2)时,它会正确显示视图。

当用户已经显示了一个视图(可能是 View1 或 View2 )时,我的问题出现了,比如说 View1 并再次单击 (Show View1) ** View1 控件保持相同的值,我想显示与所有控件相同的视图,就好像它已重新启动一样。 (我知道我可以重置用户控件 (View1) 中的所有控件,但我的应用程序需要在每次单击按钮时都有一个新的视图实例)。

不知何故,在同一场景中,当用户在视图 1 中切换到视图 2 并返回到视图 1 时,会创建一个新实例并按我的意愿显示。我想获得相同的结果,而无需切换到视图 2 并返回。

这是该问题的最小可重现示例:

ViewModelLocator.cs

using CommonServiceLocator;
using GalaSoft.MvvmLight.Ioc;

namespace SidebarApp.ViewModel
{
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            // Register viewmodels
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<View1ViewModel>();
            SimpleIoc.Default.Register<View2ViewModel>();
        }

        public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(); } }

        public View1ViewModel View1Vm { get { return ServiceLocator.Current.GetInstance<View1ViewModel>(); } }

        public View2ViewModel View2Vm { get { return ServiceLocator.Current.GetInstance<View2ViewModel>(); } }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
}

MainViewModel.cs

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows.Input;

namespace SidebarApp.ViewModel
{
    public class MainViewModel : ViewModelBase
    {

        // Commands
        public ICommand ShowView1Command { get; private set; }
        public ICommand ShowView2Command { get; private set; }

        /// <summary>
        /// Property that will cointain the current viewmodel to show
        /// ViewModelBase inplements ObservableObject class which imlements INotifyPropertyChanged
        /// </summary>
        public ViewModelBase CurrentViewModel { get; set; }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            this.ShowView1Command = new RelayCommand(ShowView1);
            this.ShowView2Command = new RelayCommand(ShowView2);
        }

        private void ShowView1()
        {
            // I tried this but it doesn't work
            //CurrentViewModel = null or View2ViewModel;

            CurrentViewModel = new View1ViewModel();

        }
        private void ShowView2()
        {
            CurrentViewModel = new View2ViewModel();
        }

    }
}

MainWindow.xaml

<Window x:Class="SidebarApp.MainWindow"
        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"
        xmlns:local="clr-namespace:SidebarApp"
        mc:Ignorable="d"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        Title="MainWindow" Height="348.965" Width="560.683">

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:View1ViewModel}">
            <local:View1View />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:View2ViewModel}">
            <local:View2View />
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.3*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <Button Command="{Binding ShowView1Command}">Show View1</Button>
            <Button Command="{Binding ShowView2Command}">Show View2</Button>            
        </StackPanel>
        <ContentControl Grid.Column="1" Content="{Binding Path=CurrentViewModel}"></ContentControl>
    </Grid>
</Window>

View1View.xaml

<UserControl x:Class="SidebarApp.View1View"
             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:local="clr-namespace:SidebarApp"
             mc:Ignorable="d" 
             DataContext="{Binding View1Vm, Source={StaticResource Locator}}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="LightPink">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
            <TextBox HorizontalAlignment="Stretch" Text="Original text value"/>
            <CheckBox Content="Some boolean value"/>
        </StackPanel>
    </Grid>
</UserControl>

View1ViewModel.cs

using GalaSoft.MvvmLight;

namespace SidebarApp
{
    public class View1ViewModel : ViewModelBase
    {
        public View1ViewModel()
        {

        }
    }
}

View2View.xaml

<UserControl x:Class="SidebarApp.View2View"
             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:local="clr-namespace:SidebarApp"
             mc:Ignorable="d" 
             DataContext="{Binding View2Vm, Source={StaticResource Locator}}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="LightCyan">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Welcome to View 2" FontSize="20"/>
            <TextBox HorizontalAlignment="Stretch" Text="Some text in view2"/>
            <Button Content="Button"/>
        </StackPanel>
    </Grid>
</UserControl>

View2ViewModel.cs

using GalaSoft.MvvmLight;

namespace SidebarApp
{
    public class View2ViewModel : ViewModelBase
    {
        public View2ViewModel()
        {

        }
    }
}

App.xaml

<Application x:Class="SidebarApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SidebarApp" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
  <Application.Resources>
    <ResourceDictionary>
      <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:SidebarApp.ViewModel" />
    </ResourceDictionary>
  </Application.Resources>
</Application>

例如,我进入 view1 并将文本框的值(“原始文本值”)更改为另一个值,然后在 Show View1 中再次单击文本,用户控件中的所有内容都保持不变,但是当我切换到 view2并返回 view1 显示一个新的用户控件及其原始值。

【问题讨论】:

  • 您是否尝试过在 ViewModelLocator 的 getter 中返回一个新的 ViewModel 实例(而不是当前实例)?
  • 我刚试过不工作。我刚刚在调试模式下意识到,第一次显示 view1 它调用 View1View.xaml.cs 的构造函数,但不再调用第二次。我不知道为什么。并创建了一个新的视图模型实例(没问题)我认为问题出在视图中,不确定。
  • 你有这个项目在 github 上的机会吗?
  • 确定我上传了项目:github.com/alex-delacruz/SidebarApp
  • 好的。如果我发现一些有趣的东西,我会看看并发布。

标签: c# .net wpf mvvm mvvm-light


【解决方案1】:

这可能感觉像是一种解决方法,但它确实有效。希望它会被证明是有用的。 (显然对 View2View 视图/视图模型进行相同的更改)。


ViewModelLocator.cs

 public View1ViewModel View1Vm { get { return new View1ViewModel(); } }


MainViewViewModel.cs

 private void ShowView1()
 {            
    CurrentViewModel = new ViewModelLocator().View1Vm;           
 }


View1View.xaml

<UserControl x:Class="SidebarApp.View1View"
             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:local="clr-namespace:SidebarApp"
             mc:Ignorable="d"                  
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="LightPink">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
            <TextBox HorizontalAlignment="Stretch" Text="{Binding Path=View1Text, Mode=TwoWay}"/>
            <CheckBox Content="Some boolean value" IsChecked="{Binding Path=CheckBoxChecked, Mode=TwoWay}"/>
        </StackPanel>
    </Grid>
</UserControl>

View1ViewModel.cs

public class View1ViewModel : ViewModelBase
{
        private string _view1Text;
        private bool _checkBoxedChecked;

        public string View1Text
        {
            get
            {
                return _view1Text;
            }
            set
            {
                _view1Text = value;
                RaisePropertyChanged("View1Text");                
            }
        }

        public bool CheckBoxChecked
        {
            get
            {
                return _checkBoxedChecked;
            }
            set
            {
                _checkBoxedChecked = value;
                RaisePropertyChanged("CheckBoxChecked");
            }
        }

        public View1ViewModel()
        {
            View1Text = "Original text value";
        }
}

【讨论】:

  • 这可能是一个解决方案,但在我的实际应用程序中,有一个用户可以修改的数据网格(行的大小、列的宽度等),但是当您想要重置整个视图时,必须重新定义数据网格作为新的没有修改,仅更改数据(视图模型)只会刷新数据,但不会刷新控件本身。我想我看到了问题,显然内容控件需要一个触发器,允许您再次呈现视图,没有这个,您只能在视图更改为另一个视图时重新创建视图,感谢您的帮助,非常感谢。
猜你喜欢
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
  • 2013-12-27
相关资源
最近更新 更多