WPF MVVM在viewmodel之间传递数据,但不更新属性

WPF MVVM在viewmodel之间传递数据,但不更新属性,wpf,mvvm,Wpf,Mvvm,我正在制作一个窗口来管理使用笔记本电脑的用户。我有一个名为LaptopWindow的窗口,其中包含一个文本框,用于显示使用它的用户的用户id。我制作了一个按钮来打开名为FindEmployeeUC的新UserControl,通过选择UserControl的DataGrid中的行并将其传递回LaptoWindow中的文本框来查找EmpID 我获得了DataGrid的选定行,并使用属性名SelectedUA将其保存在视图模型UserAccountViewModel中 当OnPropertyChang

我正在制作一个窗口来管理使用笔记本电脑的用户。我有一个名为LaptopWindow的窗口,其中包含一个文本框,用于显示使用它的用户的用户id。我制作了一个按钮来打开名为FindEmployeeUC的新UserControl,通过选择UserControl的DataGrid中的行并将其传递回LaptoWindow中的文本框来查找EmpID

我获得了DataGrid的选定行,并使用属性名SelectedUA将其保存在视图模型UserAccountViewModel中

当OnPropertyChanged事件触发时,我调用LaptoManagementViewModel的实例,该视图模型与LaptoWindow绑定,并通过名为ReceiverID的属性将EmpID设置为LaptoWindow中的文本框

属性ReceiverID获得值,但LaptoWindow的UI未获得更新

我尝试使用委托、单例模式,结果是一样的

这里有一些代码来解释我面临的更多问题

LaptopWindow xaml: FindEmployeeUC xaml: 我希望在LaptoWindow中获取文本框的EmpID。我附上一张图片以了解更多细节: 提前谢谢

在OnPropertyChanged事件invocator中,您始终在创建UserAccountViewModel的新实例。XAML代码中从未引用过此实例,因此视图无法看到此新实例。 由于视图模型有一个状态,您通常使用单个实例作为绑定目标

我删除了参数化构造函数以启用XAML中的实例化。实例从XAML分配给UserAccountVM属性,并且还从UserAccountViewModel中删除了对LaptoManagementViewModel的引用。我创建了视图模型实例,并将它们添加到App.xaml的ResourceDictionary中。 我还向LaptoManagementViewModel添加了一个PropertyChanged事件处理程序,以侦听UserAccountViewModel.SelectedUA的更改

强烈建议避免使用字符串文字。您不应该调用OnPropertyChangedMyProperty,而应该通过应用:OnPropertyChangednameofMyClass.MyProperty来使用免费编译器支持。我替换了相应的代码。您现在可以摆脱打字错误,并获得编译器检查和重构工具(例如重命名)的完全支持

也要远离单身人士。它们闻起来很香

最后一个抱怨:使字段始终是私有的或受保护的,特别是当它们是财产支持字段时。如果不使用任何访问修饰符,则内部将隐式应用。这相当于共享程序集中的public,并且永远不应该公开字段

:

通常,您应该仅对具有私有或受保护可访问性的变量使用字段。类向客户机代码公开的数据应该通过方法、属性和索引器提供。通过使用这些结构间接访问内部字段,可以防止无效的输入值。存储由公共属性公开的数据的私有字段称为后备存储或后备字段

App.xaml

UserAccountViewModel.cs


谢谢你的帮助。它工作得很好。我对私有和受保护的访问修饰符有更多的了解。我还学习了一种在ViewModel之间传输数据的新方法。我搜索了一些解决方案和论坛,以找到我的问题的解决方案。他们使用PRISM的EventAggregator将数据从这个ViewModel传输到另一个ViewModel,但这对我来说太复杂了。
<StackPanel Grid.Row="2" Grid.Column="1" Style="{StaticResource inputControl}">
      <TextBlock Text="Người nhận"/>
      <TextBox Name="txtReceiver" Text="{Binding ReceiverID,Source={StaticResource vmLaptopManagement}}" Margin="0,0,30,0"/>   
</StackPanel>

<!--Button open FindEmpUC -->
<Button Grid.Row="2" Grid.Column="1"  Width="30" Height="29" HorizontalAlignment="Right" VerticalAlignment="Bottom" Background="Transparent" Margin="0,4,4,4" Command="{Binding CmdFindEmp}">
      <Image Source="/imgs/find-48.png" Stretch="Uniform"  />
</Button>
//the userAccountVM
      UserAccountViewModel userAccountVM;

//the constructor
  public LaptopManagementViewModel(UserAccountViewModel userAccountVM)
        {
            LstDVUS = LaptopManagementBLL.Instance.GetDVUsageStatuses();           
            LstLaptop = LaptopManagementBLL.Instance.GetLaptopsInfo();

            this.userAccountVM = userAccountVM;
            ReceiverID = this.userAccountVM.SelectedUA.EmpID;
        }
//the ReceiverID property
        string receiverID;

        public string ReceiverID
        {
            get { return receiverID; }
            set
            {
                receiverID = value;
                OnPropertyChanged("ReceiverID");
            }
        }
//function open FindEmployeeUC
 private void FindEmployee(object obj)
        {
            //show findEmployee UC
            Window wd = new Window()
            {
                Content = new FindEmployeeUC(),                
            };            
            wd.ShowDialog();

        }
 <DataGrid Grid.Row="1" ItemsSource="{Binding LstUA}" CanUserAddRows="False" SelectedItem="{Binding SelectedUA,Mode=TwoWay}" AutoGenerateColumns="False" ColumnWidth="*" IsReadOnly="True">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding ID}"></DataGridTextColumn>
                <DataGridTextColumn Header="EmpID" Binding="{Binding EmpID}"></DataGridTextColumn>
                <DataGridTextColumn Header="EmpName" Binding="{Binding EmpName}"></DataGridTextColumn>
                <DataGridTextColumn Header="Position" Binding="{Binding Position}"></DataGridTextColumn>
                <DataGridTextColumn Header="LineGroup" Binding="{Binding LineGroup}"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
//The property "SelectedUA"
        UserAccountModel selectedUA;

        public UserAccountModel SelectedUA
        {
            get { return selectedUA; }
            set
            {
                if(selectedUA!=value)
                {
                    selectedUA = value;
                    LaptopManagementViewModel laptopVM = new LaptopManagementViewModel(this);

                    OnPropertyChanged("SelectedUA");

                }

            }
        }
<Application x:class="App">
  <Application.Resources>
    <ResourceDictionary>

      <UserAccountViewModel x:Key="UserAccountViewModel" />

      <LaptopManagementViewModel x:Key="LaptopManagementViewModel">
        <LaptopManagementViewModel.UserAccountVM>
          <StaticResource ResourceKey="UserAccountViewModel" />
        </LaptopManagementViewModel.UserAccountVM>
      </LaptopManagementViewModel>

  </Application.Resources>
</Application>
<Window x:class="LaptopWindow">
  <Window.DataContext>
    <StaticResource ResourceKey="LaptopManagementViewModel" />
  </Window.DataContext>

  ...
</Window>
<Window x:class="FindEmployeeUC">
  <Window.DataContext>
    <StaticResource ResourceKey="UserAccountViewModel" />
  </Window.DataContext>

  <DataGrid 
    ...
  </DataGrid>
</Window>
public class LaptopManagementViewModel
{
  private UserAccountViewModel userAccountVM;
  public UserAccountViewModel UserAccountVM
  {
    get => userAccountVM; 
    set
    {
      userAccountVM = value;
      OnPropertyChanged(nameof(UserAccountVM));

      if (userAccountVM != null)
      {
        // Always clean up event handlers to avoid memory leaks
        userAccountVM.PropertyChanged -= UpdateReceiverIdOnPropertyChanged;
      }

      userAccountVM.PropertyChanged += UpdateReceiverIdOnPropertyChanged;
    }
  }

  // The constructor is now parameterless for the use in XAML
  public LaptopManagementViewModel()
  {
    LstDVUS = LaptopManagementBLL.Instance.GetDVUsageStatuses();
    LstLaptop = LaptopManagementBLL.Instance.GetLaptopsInfo();
  }

  // UserAccountVM.PropertyChanged event handler
  private void UpdateReceiverIdOnPropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    if (e.PropertyName.Equals(nameof(UserAccountViewModel.SelectedUA), StringComparison.OrdinalIgnoreCase))
    {
      ReceiverID = UserAccountVM.SelectedUA.EmpID;    
    }
  }

  private string receiverID;
  public string ReceiverID
  {
    get { return receiverID; }
    set
    {
      receiverID = value;
      OnPropertyChanged(nameof(ReceiverID));
    }
  }
}
public class UserAccountViewModel
{
  private UserAccountModel selectedUA;
  public UserAccountModel SelectedUA
  { 
    get => selectedUA;
    set
    {
      if(selectedUA!=value)
      {
        // Removed wrong creation of LaptopManagementViewModel instances
        selectedUA = value;
        OnPropertyChanged(nameof(SelectedUA));
      }
    } 
  }
}