更改视图模型的WPF MVVM调用旧视图模型上的依赖属性

本文关键字:视图 模型 依赖 属性 WPF MVVM 调用 | 更新日期: 2023-09-27 18:28:25

我有一个TabViewModel,它包含一个依赖属性CurrentViewModel。CurrentViewModel属性绑定到视图TabView.xaml中的ContentControl。TabViewModel还包含一个将CurrentViewModel更改为ProductViewModel的命令:

 public class TabViewModel: BaseViewModel
{
    public string TabName { get; set; }
    //public List<BaseViewModel> ViewModels { get; set; }
    private PageViewModel _currentViewModel;
    public PageViewModel CurrentViewModel
    {
        get { return _currentViewModel; }
        set
        {
            _currentViewModel = value;
            OnPropertyChanged("CurrentViewModel");
        }
    }
    public TabViewModel(string tabName, PageViewModel currentViewModel)
    {
        TabName = tabName;
        CurrentViewModel = currentViewModel;
    }
    private ICommand _navigateToProductViewModelCommand;
    public ICommand NavigateToProductViewModelCommand
    {
        get
        {
            if (_navigateToProductViewModelCommand == null)
            {
                _navigateToProductViewModelCommand = new DelegateCommand<Product>(
                      (p) =>
                      {
                          CurrentViewModel = new ProductViewModel();
                      });
            }
            return _navigateToProductViewModelCommand;
        }
    }
}

选项卡视图.xaml

<UserControl x:Class="Monitoring_Tool.Views.TabView"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <ProgressBar Value="{Binding Path=CurrentViewModel.PageProgress}" Height="5" Grid.Row="0" Margin="0,0,0,10">
        <ProgressBar.Style>
            <Style TargetType="{x:Type ProgressBar}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ProgressBar">
                            <Border  BorderThickness="0,0,0,0" Background="LightGray" CornerRadius="0" Padding="0">
                                <Grid x:Name="PART_Track">
                                    <Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="#00B6FA" />
                                </Grid>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ProgressBar.Style>
    </ProgressBar>
    <ContentControl Content="{Binding Path=CurrentViewModel}" Grid.Row="1" />
</Grid>

我这样实例化TabViewModel:

new TabViewModel("Producten", new ProductsViewModel())

ProductsView.xaml显示为应该显示的样子。在ProductsView.xsaml中,我从TabViewModel调用命令,如下所示:

<DataGrid.InputBindings>
            <MouseBinding
            MouseAction="LeftDoubleClick"
                Command="{Binding DataContext.NavigateToProductViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type views:TabView}}}"/>
        </DataGrid.InputBindings>

当数据网格为空时,命令会被执行,并且ProductView.xaml看起来像它应该的样子。但是当数据网格不为空时会发生一些奇怪的事情:

命令被执行,当我调试时,我可以看到currentViewModel被更改为ProductViewModel。然后当调用OnPropertyChanged("CurrentViewModel")时。ProductsViewModel上存在对dependency属性(SelectedAssetCategory)的集合调用(值=null),该属性已被替换,不再存在?!

当我将CurrentViewModel设置为null时,同样的事情也会发生,我只能执行CurrentViewModel=new ProductsViewModel。所以我想这是更新UI的问题吗?

在App.xaml中,我定义了以下资源:

  <DataTemplate DataType="{x:Type viewmodels:TabViewModel}">
        <views:TabView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodels:ProductsViewModel}">
        <views:ProductsView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodels:ProductViewModel}">
        <views:ProductView />
    </DataTemplate>

ProductsViewModel如下所示:

    class ProductsViewModel: PageViewModel
{
    private readonly MonitotingToolEntities _databaseEntities;
    public ProductsViewModel()
    {
          _databaseEntities = new MonitotingToolEntities();
        AssetCategories = new ObservableCollection<AssetCategory>(_databaseEntities.AssetCategory.ToList())
        {
            new AssetCategory() {AssetCategoryID = 0, AssetCategoryName = "Alles"}
        };
        Results = new ObservableCollection<Product>();
    }
    public ObservableCollection<AssetCategory> AssetCategories { get; set; }
    private AssetCategory _selectedAssetCategory;
    public AssetCategory SelectedAssetCategory
    {
        get { return _selectedAssetCategory; }
        set
        {
            _selectedAssetCategory = value; //this one is called with value = null
            OnPropertyChanged("SelectedAssetCategory");
            Filter();
        }
    }
    public ObservableCollection<Product> Results { get; set; }
    public void Filter()
    {
        Results.Clear();
        List<Product> products =
            SelectedAssetCategory.AssetCategoryID == 0
                ? _databaseEntities.Product.ToList()
                : SelectedAssetCategory.Product.ToList();
        foreach (Product product in products)
        {
            Results.Add(product);
        }
    }
}

ProductsView.xaml:

<UserControl x:Class="Monitoring_Tool.Views.ProductsView"
         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:views="clr-namespace:Monitoring_Tool.Views"
         xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
         xmlns:sys="clr-namespace:System;assembly=mscorlib"
         xmlns:viewModels="clr-namespace:Monitoring_Tool.ViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>

    <CollectionViewSource  x:Key="CvsAssetCategories" Source="{Binding Path= AssetCategories}" >
        <CollectionViewSource.SortDescriptions>
            <componentModel:SortDescription PropertyName="AssetCategoryID"/>
        </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>
    <CollectionViewSource x:Key="CvsResults" Source="{Binding Path= Results}" >
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="AssetCategory.AssetCategoryName" />
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
    <Style TargetType="Image" x:Key="ImageDisabledStyle">
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Opacity" Value="0.5" />
            </Trigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="auto" />
    </Grid.RowDefinitions>
    <Grid Margin="0, 0, 0, 10" Grid.Row="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="auto"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="Asset Categorie:" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,10,0"/>
        <ComboBox Grid.Column="1"
                  ItemsSource="{Binding Source={StaticResource CvsAssetCategories}}"
                  DisplayMemberPath="AssetCategoryName"
                  SelectedItem="{Binding SelectedAssetCategory}"
                  Margin="0,0,10,0"/>
        <TextBlock Text="Zoeken:" Grid.Column="3" VerticalAlignment="Center" Margin="0,0,10,0"/>
        <ComboBox Grid.Column="4" 
                  SelectedItem="{Binding SelectedSearchField}"
                  Margin="0,0,10,0"/>
        <TextBox Text="{Binding Path=SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                 Grid.Column="5" Margin="0,0,10,0">
            <TextBox.InputBindings>
                <KeyBinding Command="{Binding Path=SearchCommand}" CommandParameter="{Binding SearchQuery}" Key="Enter" />
            </TextBox.InputBindings>
        </TextBox>
        <Button Grid.Column="6"
                Command="{Binding SearchCommand}" 
                CommandParameter="{Binding SearchQuery}"
                Padding="5,0,5,0" Margin="0,0,10,0" >
            <Button.Content>
                <Image Source="/Recourses/SearchIcon.png"
                    Stretch="None" VerticalAlignment="Top" Style="{Binding Source={StaticResource ImageDisabledStyle}}"/>
            </Button.Content>
        </Button>
        <Button Grid.Column="7"
                Command="{Binding CancelSearchCommand}"
                IsEnabled="{Binding CancelSearchEnabled}"
                Padding="5,0,5,0">
            <Button.Content>
                <Image Source="/Recourses/CancelSearchIcon.png" 
                    Stretch="None" VerticalAlignment="Top" Style="{Binding Source={StaticResource ImageDisabledStyle}}"/>
            </Button.Content>
        </Button>
    </Grid>

    <DataGrid Name="DgProducts" AutoGenerateColumns="False" 
              RowHeaderWidth="0" Margin="0,0,0,10" Grid.Row="1" IsReadOnly="True"
              SelectionMode="Single" CanUserReorderColumns="False"
              EnableRowVirtualization="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True"
              ItemsSource="{Binding Source={StaticResource CvsResults}}" SelectedItem="{Binding SelectedProduct}">
        <DataGrid.CellStyle>
            <Style TargetType="DataGridCell">
                <Setter Property="BorderThickness" Value="0"/>
            </Style>
        </DataGrid.CellStyle>
        <DataGrid.InputBindings>
            <MouseBinding
            MouseAction="LeftDoubleClick"
                Command="{Binding DataContext.NavigateToProductViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type views:TabView}}}"
                />
        </DataGrid.InputBindings>
        <DataGrid.Resources>
            <Style TargetType="DataGridColumnHeader" x:Key="DgVerticalColumnHeader">
                <Setter Property="LayoutTransform">
                    <Setter.Value>
                        <RotateTransform Angle="270" />
                    </Setter.Value>
                </Setter>
            </Style>
            <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" 
               Color="LightGray"/>
            <SolidColorBrush  x:Key="{x:Static SystemColors.HighlightTextBrushKey }" 
               Color="Black"/>
        </DataGrid.Resources>
        <DataGrid.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="GroupItem">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="GroupItem">
                                    <StackPanel>
                                        <TextBlock Text="{Binding Path=Name}" Background="DarkGray" Padding="2,0,0,0"/>
                                        <ItemsPresenter/>
                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </DataGrid.GroupStyle>
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=Manager.ManagerName}" Header="Manager" />
            <DataGridTextColumn Binding="{Binding Path=ProductName}" Header="Product" />
            <DataGridTextColumn Binding="{Binding Path=MonitoringBy}" Header="Monitoring door" />
            <DataGridTextColumn Binding="{Binding Path=AumProduct}" Header="AUM Product (mln)"  />
            <DataGridTextColumn Binding="{Binding Path=AumProductDate, StringFormat='{}{0:dd-MM-yyyy}'}" Header="Datum AUM Product" />
            <DataGridTextColumn Binding="{Binding Path=AumStrategy}" Header="AUM Strategie (mln)" />
            <DataGridTextColumn Binding="{Binding Path=AumStrategyDate, StringFormat='{}{0:dd-MM-yyyy}'}" Header="Datum AUM Strategie" />
            <DataGridTextColumn Binding="{Binding Path=Aum}" Header="AUM (mln)" />
            <DataGridTextColumn Binding="{Binding Path=TotalExpenseRatio}" Header="TER (bp)" />
            <DataGridTextColumn Binding="{Binding Path=Fee}" Header="Total Fee" />
    </DataGrid>
    <Grid Grid.Row="2">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="auto" />
        </Grid.ColumnDefinitions>
        <TextBlock Text="{Binding Path=Results.Count, StringFormat='{}{0} Producten'}" Grid.Column="0" Margin="0,0,0,0"/>
        <Button Grid.Column="2"
                Content="Toevoegen"
                Padding="5,0,5,0" Margin="0,0,10,0"
                Command="{Binding AddProductCommand}" />
        <Button Grid.Column="3"
                Content="Verwijderen" 
                Padding="5,0,5,0"
                Command="{Binding Path=RemoveProductCommand}" 
                CommandParameter="{Binding Path=SelectedProduct}"/>
    </Grid>
</Grid>

PageViewModel是一个抽象类:

 public abstract class PageViewModel: BaseViewModel
{
    private int _pageProgress;
    public int PageProgress
    {
        get { return _pageProgress; }
        set
        {
            _pageProgress = value;
            OnPropertyChanged("PageProgress");
        }
    }
}

更改视图模型的WPF MVVM调用旧视图模型上的依赖属性

这有点奇怪,但它与绑定到组合框的collectionviewsource(CvsAssetCategories)有关。如果我直接绑定到组合框而不使用collectionviewsource,则不会调用依赖项属性。但是,我喜欢使用collectionviewsource作为排序描述符。我现在的解决方案是从依赖属性对setter进行非空检查,但我认为这是一种糟糕的做法。