【问题标题】:Binding Viewmodel to View将 Viewmodel 绑定到 View
【发布时间】:2020-02-05 08:48:42
【问题描述】:

MainWindow.xaml

<Window x:Class="SDT.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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        mc:Ignorable="d"
        xmlns:viewModels="clr-namespace:SDT.ViewModels"
        Height="500" Width="700" WindowStyle="None" AllowsTransparency="False" ResizeMode="NoResize" Background="#FF2C2C2C"
        TextElement.Foreground="{DynamicResource MaterialDesignBody}" TextElement.FontWeight="SemiBold">

    <Window.DataContext>
        <viewModels:UserViewModel />
    </Window.DataContext>

    <Grid>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="308,90,0,0" TextWrapping="Wrap" Text = "{Binding Login}" VerticalAlignment="Top" Width="120"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="152,200,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
        <Button Content="Submit" Command="{Binding SubmitLoginDataCommand}" HorizontalAlignment="Left" Margin="567,259,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>

MainWindows.cs

public partial class MainWindow : Window
{
    UserViewModel userViewModel = new UserViewModel();
    public MainWindow()
    {
        InitializeComponent();
        DataContext = userViewModel;

    }
}

用户视图模型

public class UserViewModel : INotifyPropertyChanged
{
    private UserService userService = new UserService();

    public string _firstName;

    public string Login { get; set; }

    public void SubmitLoginData(object loginData)
    {
        userService.CheckUserExist(Login);
    }

    public ICommand SubmitLoginDataCommand => new RelayCommand(SubmitLoginData, param => true);

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName != value)
            {
                _firstName = value;
                OnPropertyChanged("FirstName");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

你好。 FirstName 绑定有什么问题? 文本框什么都不显示。 public string FirstName{} - 这里的 FirstName 在调试中具有价值。 我尝试不使用 Window.DataContext 并且仅使用 Text="{Binding FirstName}" 但没有成功。 登录绑定工作正常。

【问题讨论】:

  • 我曾经遇到过类似的问题,是由隐形字符引起的。如果您注释掉/删除有问题的行,复制登录绑定的行并将绑定从“登录”更改为“名字”,您是否仍然遇到同样的问题?
  • 顺便说一句,如果你使用OnPropertyChanged(nameof(FirstName))而不是硬编码属性名,你的代码会更不容易出错。
  • @Frauke 很遗憾,没有变化
  • 尝试初始化userviewmodel的构造函数中的属性,并从后面的代码中删除数据上下文代码。

标签: c# mvvm data-binding


【解决方案1】:

您需要从 MainWindow.xaml 中删除这部分:

<Window.DataContext> <viewModels:UserViewModel /> </Window.DataContext>

因为你有两次 DataContext, 在 xaml 和 cs 中,所以不知道从哪里获取数据。

【讨论】:

    【解决方案2】:

    我想发布第二个答案,以提出我认为您会非常喜欢的建议。我们可以让您的 OnPropertyChanged 事件自动命名属性,让您只需编写“OnPropertyChanged()”即可触发 UI 更新。

    为此,我们将使用一个名为“调用者成员名称”的属性 - 它可以按照您的想法执行 - 提供称为代码的对象或属性的名称!

    要使用它,我们需要在 UserViewModel 类的顶部添加一条 using 语句:

    使用 System.Runtime.CompilerServices;

    然后,我们将修改您的 OnPropertyChanged 事件以使用“调用者成员名称”除非您指定特定名称。现在应该是这样的:

        private void OnPropertyChanged([CallerMemberName] String name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    

    现在 - 我们将更新您的属性以使用简化方法:

       public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName != value)
            {
                _firstName = value;
                OnPropertyChanged();
            }
        }
    }
    

    最后 - 我最近学习了一种我更喜欢的获取/设置的替代方法。你正在做的是完全可以的,没有必要改变它,但我建议你试试看你喜欢它:)

        public string FirstName
        {
            get => _firstName; 
            set
            {
                if (_firstName == value) return;
                _firstName = value;
                OnPropertyChanged(); 
            }
        }
    

    原因:我发现按 == 而不是 != 更快,括号更少。如果名字等于值,它将简单地返回(退出)。如果没有,它会跳过返回!我喜欢那个!

    【讨论】:

      【解决方案3】:

      让我们测试您的绑定!让我们在表单中添加一个文本块,并将 FirstName 属性绑定到它。如果您的绑定工作正常,您在文本中输入的任何内容都应显示在文本中。

      您的 MainWindow.xaml 应如下所示:

      <Window x:Class="SDT.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:viewModels="clr-namespace:Junk.cats"
          mc:Ignorable="d"
          Title="MainWindow" Height="450" Width="800">
      
      <Grid>
          <TextBox HorizontalAlignment="Left" Height="23" Margin="308,90,0,0" TextWrapping="Wrap" Text = "{Binding Login}" VerticalAlignment="Top" Width="120"/>
          <TextBox HorizontalAlignment="Left" Height="23" Margin="152,200,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
          <Button Content="Submit" Command="{Binding SubmitLoginDataCommand}" HorizontalAlignment="Left" Margin="567,259,0,0" VerticalAlignment="Top" Width="75"/>
          <TextBlock HorizontalAlignment="Left" Height="32" Margin="140,247,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="346"> 
             <Run Text="First Name: "/>
             <Run Text="{Binding Path=FirstName}"/>
          </TextBlock>
      </Grid>
      

      我希望这个测试能够奏效,并且您会发现您的 get/set 属性和 UI 更新没有问题。我相信您的问题现在与 UserViewModel 的“实例”(副本)有关。

      假设我们正在处理一个打印文档。当您使用 = new UserService();作业,您正在打印我们文件的新副本。如果我们打印一个新文档并将其提供给 MainWindow.cs(我们称其为“Bob”),AND 然后您在 userService 代码中打印一个新副本(我们称其为“Frank”) - 这些是文档的两个独立实例/副本。

      我们需要制作一次这个对象,并告诉“Bob”和“Frank”使用该对象的同一个副本。不用担心,这比您想象的要容易,并且您会在使用时开始习惯它。

      我将使用一些 STATIC 字段来简化您的故障排除 - 您不需要创建静态实例来完成这项工作,但您需要确保您的共享实例课程可供任何需要的人使用。

      第 1 步 - 创建一个新类,我们称之为“视图”。 第 2 步 - 使类公开静态 第 3 步 - 在此处创建 Public static userViewModel

      public static class views
      {
          public static UserViewModel userViewModel = new UserViewModel();
      }
      

      现在 - 让我们更改 MainWindow.cs 以使用此类的共享实例:

          public partial class MainWindow : Window
      {
      
      
          public MainWindow()
          {
              InitializeComponent();
              DataContext = views.userViewModel;
      
          }
      }
      

      您需要做的最后一件事 - 让您的外部函数与“userViewModel”的相同副本一起工作!我没有你的代码,所以我假装你的函数叫做'YourFunctionNToChangeTheName',它位于你的'UserService'类中:

          public class UserService
      {
         public void YourFunctionToChangeTheName()
          {
              views.userViewModel.FirstName = "FRANK"; 
          }
      }
      

      这里要发现的关键是您没有创建新的“UserViewModel” - 您正在重新使用 MainWindow.cs 绑定到的 相同实例 - 所以 UI现在正在收到“财产变更通知”!

      请记住,UserViewModel(类)本身不是静态的,我们创建了它的共享/静态实例,可以从程序中的任何位置访问它。我建议了这种方法,以便您可以学习实例的基础知识:)

      祝你好运!!

      【讨论】:

      • 快到了,但到目前为止。是的,_firstName 是私有的。我只是忘记改变但不起作用。第二个答案也是。
      • @Hoshie 让我知道这是否适合您 - 您的绑定工作正常,我相信您无法从外部类更新 :)
      猜你喜欢
      • 1970-01-01
      • 2012-03-18
      • 1970-01-01
      • 1970-01-01
      • 2015-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-29
      相关资源
      最近更新 更多