WPF MVVM中的字符串属性是不可变的吗?

发布于 2025-02-09 19:42:53 字数 4064 浏览 1 评论 0 原文

我的模型:

sealed class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

我的ViewModel:

// INotifyPropertyChanged notifies the View of property changes, so that Bindings are updated.

密封类MyViewModel:InotifyPropertychanged { 私人用户;

public string FirstName { 
    get {return user.FirstName;} 
    set {
        if(user.FirstName != value) {
            user.FirstName = value;
            OnPropertyChange("FirstName");
            // If the first name has changed, the FullName property needs to be udpated as well.
            OnPropertyChange("FullName");
        }
    }
}

public string LastName {
    get { return user.LastName; }
    set {
        if (user.LastName != value) {
            user.LastName = value;
            OnPropertyChange("LastName");
            // If the first name has changed, the FullName property needs to be udpated as well.
            OnPropertyChange("FullName");
        }
    }
}

// This property is an example of how model properties can be presented differently to the View.
// In this case, we transform the birth date to the user's age, which is read only.
public int Age { 
    get {
        DateTime today = DateTime.Today;
        int age = today.Year - user.BirthDate.Year;
        if (user.BirthDate > today.AddYears(-age)) age--;
        return age;
    }
}

// This property is just for display purposes and is a composition of existing data.
public string FullName {
    get { return FirstName + " " + LastName; }
}

public MyViewModel() {
    user = new User {
        FirstName = "John",
        LastName = "Doe",
        BirthDate = DateTime.Now.AddYears(-30)
    };
}

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChange(string propertyName) {
    if(PropertyChanged != null) {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

然后,在XAML中,我们使用绑定:

<Grid>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
     <RowDefinition Height="Auto"/>
     <RowDefinition Height="Auto"/>
 </Grid.RowDefinitions>

<TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="4" Text="{Binding FullName}" HorizontalAlignment="Center" FontWeight="Bold"/>

<Label Grid.Column="0" Grid.Row="1" Margin="4" Content="First Name:" HorizontalAlignment="Right"/>
<!-- UpdateSourceTrigger=PropertyChanged makes sure that changes in the TextBoxes are immediately applied to the model. -->
  <TextBox Grid.Column="1" Grid.Row="1" Margin="4" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Width="200"/>

  <Label Grid.Column="0" Grid.Row="2" Margin="4" Content="Last Name:" HorizontalAlignment="Right"/>
  <TextBox Grid.Column="1" Grid.Row="2" Margin="4" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left"  Width="200"/>

   <Label Grid.Column="0" Grid.Row="3" Margin="4" Content="Age:" HorizontalAlignment="Right"/>
   <TextBlock Grid.Column="1" Grid.Row="3" Margin="4" Text="{Binding Age}" HorizontalAlignment="Left"/>

</Grid>

背后的代码:

public partial class MainWindow : Window
{
    private readonly MyViewModel _viewModel;

    public MainWindow() {
        InitializeComponent();
        _viewModel = new MyViewModel();
        // The DataContext serves as the starting point of Binding Paths
        DataContext = _viewModel;
    }
}

我的问题是用于绑定。例如,如果更改 firstName ,则视图也会更改。我们知道C#中的字符串是不变的,当值更改时,创建了新对象。但是在WPF绑定中,如果更改了 firstName 是创建了一个新的名称对象吗?我认为这是没有的,因为我们使用绑定将值保存在一个对象中。那么 onPropertyChange 内部删除不变的?

My model:

sealed class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

My ViewModel:

// INotifyPropertyChanged notifies the View of property changes, so that Bindings are updated.

sealed class MyViewModel : INotifyPropertyChanged
{
private User user;

public string FirstName { 
    get {return user.FirstName;} 
    set {
        if(user.FirstName != value) {
            user.FirstName = value;
            OnPropertyChange("FirstName");
            // If the first name has changed, the FullName property needs to be udpated as well.
            OnPropertyChange("FullName");
        }
    }
}

public string LastName {
    get { return user.LastName; }
    set {
        if (user.LastName != value) {
            user.LastName = value;
            OnPropertyChange("LastName");
            // If the first name has changed, the FullName property needs to be udpated as well.
            OnPropertyChange("FullName");
        }
    }
}

// This property is an example of how model properties can be presented differently to the View.
// In this case, we transform the birth date to the user's age, which is read only.
public int Age { 
    get {
        DateTime today = DateTime.Today;
        int age = today.Year - user.BirthDate.Year;
        if (user.BirthDate > today.AddYears(-age)) age--;
        return age;
    }
}

// This property is just for display purposes and is a composition of existing data.
public string FullName {
    get { return FirstName + " " + LastName; }
}

public MyViewModel() {
    user = new User {
        FirstName = "John",
        LastName = "Doe",
        BirthDate = DateTime.Now.AddYears(-30)
    };
}

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChange(string propertyName) {
    if(PropertyChanged != null) {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

Then in xaml we use binding:

<Grid>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
     <RowDefinition Height="Auto"/>
     <RowDefinition Height="Auto"/>
 </Grid.RowDefinitions>

<TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="4" Text="{Binding FullName}" HorizontalAlignment="Center" FontWeight="Bold"/>

<Label Grid.Column="0" Grid.Row="1" Margin="4" Content="First Name:" HorizontalAlignment="Right"/>
<!-- UpdateSourceTrigger=PropertyChanged makes sure that changes in the TextBoxes are immediately applied to the model. -->
  <TextBox Grid.Column="1" Grid.Row="1" Margin="4" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Width="200"/>

  <Label Grid.Column="0" Grid.Row="2" Margin="4" Content="Last Name:" HorizontalAlignment="Right"/>
  <TextBox Grid.Column="1" Grid.Row="2" Margin="4" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left"  Width="200"/>

   <Label Grid.Column="0" Grid.Row="3" Margin="4" Content="Age:" HorizontalAlignment="Right"/>
   <TextBlock Grid.Column="1" Grid.Row="3" Margin="4" Text="{Binding Age}" HorizontalAlignment="Left"/>

</Grid>

The code behind:

public partial class MainWindow : Window
{
    private readonly MyViewModel _viewModel;

    public MainWindow() {
        InitializeComponent();
        _viewModel = new MyViewModel();
        // The DataContext serves as the starting point of Binding Paths
        DataContext = _viewModel;
    }
}

My question is that for binding. For example, if FirstName is changed, the view is changed as well. We know string in C# is immutable, when the value changes a new object is created. But in WPF binding, if FirstName is changed, is a new FirstName object created? I assume it is NO since we use binding to hold the value in one object. So does OnPropertyChange remove the immutable internally?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

老娘不死你永远是小三 2025-02-16 19:42:53

不, firstName 未重新分配或突变。它是对被重新分配的Backing 字符串的引用。

当您拥有:

public string FirstName { get; set; }

It is compiled (roughly) into:

private string _firstName;
public string FirstName 
{
    get => _firstName;
    set => _firstName = value;
}

如您所见,将值分配给 firstName 将值作为参数传递给 setter for first> firstName reassigns _firstName 代码>(私人备份字段)。 FirstName 永远不会更改。

这也是为什么需要 InotifyProperyChanged 。由于绑定是 firstName firstName 永远不会更改的,因此框架需要一种方法来确定 first> firstName 更改的值何时。

No, FirstName is not reassigned or mutated. It is a reference to a backing string that is reassigned.

When you have an auto-implemented property:

public string FirstName { get; set; }

It is compiled (roughly) into:

private string _firstName;
public string FirstName 
{
    get => _firstName;
    set => _firstName = value;
}

As you can see, assigning a value to FirstName passes the value as an argument to the setter for FirstName which reassigns _firstName (the private backing field). FirstName is never changed.

This is also why INotifyProperyChanged is required. Because the binding is on FirstName and FirstName never changes, the framework needs a way to tell when the value referenced by FirstName changes.

要走就滚别墨迹 2025-02-16 19:42:53

在MVVM中,您可以绑定到属性(不与支持成员)。

当您更改属性(例如 String )时,您可以从技术上将新对象放置在类成员的后面(即属性的背景实例)。

onPropertychanged 告诉视图,因此它再次调用属性getter(不是字符串实例),获取新实例等...

如果您不这样做,则视图将保持并显示参考对于您的ViewModel不再使用的旧字符串

,我们有

observableCollections 可观察的类型,可以自动执行此通知,因为它们是可突变的,这意味着他们意味着他们保持相同的实例,而只有其内容会改变。

最常见的是,使用ObservableCollections来通知视图,其内容已更改(而集合实例保持不变)。 (添加了元素,删除..)

在这种情况下,您无需通知,因为该视图已注册到该事件。

如果您将新实例分配给ObservableCollection成员,则该视图也不会在没有属性汇总的情况下正确更新,因为该集合的原始实例将不再更改。

In MVVM, you bind to Properties (not to the backing members).

When you change a Property, like a string, then you technically place a new object behind the class member (i.e. the backing instance of a property).

OnPropertyChanged tells that to the View, so it calls the Property Getter (not the string instance) again, getting a the new instance etc...

If you would not, the View would hold and display a reference to the old string, which is no longer used by your Viewmodel

Then, we have

ObservableCollections and Observable types, that can do this notification automatically, because they are mutable, meaning they stay the same instance, while only their content changes.

Most commonly, ObservableCollections are used, to notify the View, that it contents has changed (while the collection instance stayed the same). (element added, removed..)

In that case, you dont need to Notify, because the View has registered to that event.

If you would assign a new instance to an observableCollection member, the view would also not update correctly without Propertychanged, because the original instance of the collection would no longer change.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文