gpt4 book ai didi

c# - WPF MVVM : Update label depending on Textbox input (2nd Attempt)

转载 作者:行者123 更新时间:2023-12-03 10:31:45 28 4
gpt4 key购买 nike

好的,我现在有一个更具体的问题。

我试图弄清楚当两个文本框不再为空时如何更改标签的值( bool 值)。我似乎无法弄清楚如何让它工作,即使它看起来很简单。

有人能指出我正确的方向吗?

请看下面我的代码。

模型(Person.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PracticeUI.Model
{
public class Person
{
private string _firstName;
private string _lastName;

public string FullName
{
get
{
return _firstName + " " + _lastName;
}
set { }
}

public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
}
}

public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
}
}
}
}

View 模型 (PersonViewModel.cs)

using PracticeUI.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace PracticeUI.ViewModel
{
public class PersonViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

private Person _newPerson = new Person();
private ICommand _addPerson;
public Person NewPerson
{
get
{
return _newPerson;
}
set
{
_newPerson = value;
OnPropertyChanged("NewPerson");
}
}

public PersonViewModel()
{
_PersonList.Add(new Person() { FirstName = "Tom", LastName = "Barratt" });
_PersonList.Add(new Person() { FirstName = "Harriet", LastName = "Hammond" });
}

private ObservableCollection<Person> _PersonList = new ObservableCollection<Person>();

public ObservableCollection<Person> PersonList
{
get
{
return _PersonList;
}
set
{
_PersonList = value;
OnPropertyChanged("PersonList");
OnPropertyChanged("AddPersonCanExecute");
}
}

public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

public ICommand AddPersonCommand
{
get
{
if (_addPerson == null)
{
_addPerson = new RelayCommand(p => this.AddPersonCanExecute, p => this.AddPerson());
}
return _addPerson;
}
}
public bool AddPersonCanExecute
{
get
{
return _newPerson.FirstName != string.Empty || _newPerson.LastName != string.Empty;
}
}

public void AddPerson()
{
_PersonList.Add(new Person() { FirstName = _newPerson.FirstName, LastName = _newPerson.LastName });
OnPropertyChanged("PersonList");
}
}
}

查看 (MainWindow.xaml)

<Window x:Class="PracticeUI.View.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:ViewModel="clr-namespace:PracticeUI.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ViewModel:PersonViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Source={StaticResource ViewModel}, Path=PersonList}" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="3" Height="200" Margin="0 0 0 20">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Label Content="First Name:" Grid.Row="2" Grid.Column="1"/>
<TextBox Text="{Binding Source={StaticResource ViewModel}, Path=NewPerson.FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Grid.Row="2" HorizontalAlignment="Left" Grid.Column="2" Height="40" Width="200" Margin="10 5"/>
<Label Content="First Name:" Grid.Row="3" Grid.Column="1"/>
<TextBox Text="{Binding Source={StaticResource ViewModel}, Path=NewPerson.LastName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Grid.Row="3" HorizontalAlignment="Left" Grid.Column="2" Height="40" Width="200" Margin="10 5"/>
<Button Command="{Binding Source={StaticResource ViewModel}, Path=AddPersonCommand}" Content="Add Person" Width="120" Height="30" Grid.Row="4" Grid.Column="2"/>
<Label Content="{Binding Source={StaticResource ViewModel}, Path=AddPersonCanExecute, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" Grid.Row="2" Grid.Column="4"/>
</Grid>
</Window>

最佳答案

一、重命名PersonViewModelMainViewModel .它不是代表一个人的 View 模型,它是整个程序的主要 View 模型。它有一个完整的集合 Person ;一个人怎么样?它不是。很好地命名你的类可以更容易地跟踪什么是什么。我们将重命名 PersonPersonViewModel因为它也需要是一个 View 模型,而且它确实代表了一个人。

您希望 UI 查看 AddPersonCanExecute 的值每当 NewPerson.FirstName 的值发生变化时或 NewPerson.LastName .

什么会导致这些值发生变化?

一种方法是 NewPerson可以换。所以:

public Person NewPerson
{
get
{
return _newPerson;
}
set
{
_newPerson = value;
OnPropertyChanged(nameof(AddPersonCanExecute));
OnPropertyChanged(nameof(NewPerson));
}
}

另一种方法是用户可以在绑定(bind)到 FirstName 的文本框中键入新值。和 LastName NewPerson 的属性.那么你和 UI 都不走运了,因为 Person不是 View 模型。当其属性发生变化时,它从不引发任何事件。所以让它成为一个 View 模型。
public class ViewModelBase : INotifyPropertyChanged
{
// Copy your INotifyPropertyChanged implementation here from your main viewmodel
// Make your main viewmodel inherit from ViewModelBase
}

// Formerly PersonViewModel
public class MainViewModel : ViewModelBase
{
// We need this to be the actual type because we'll need to be calling
// RaiseCanExecuteChanged() on it. Or whatever equivalent.
private RelayCommand _addPerson;

// All the stuff PersonViewModel had.
// Stuff
// Stuff
// Stuff
}

// Remember, your old PersonViewModel is now named MainViewModel. This is the class
// that you used to call Person.
public class PersonViewModel : ViewModelBase
{
public string FullName
{
get
{
return _firstName + " " + _lastName;
}
// No empty set, not ever. Somebody will try to set FullName and the compiler
// will let him think it worked. But nothing will change. That's a bug.
//set { }
}

public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
// Do the same for LastName. Careful you don't pass nameof(FirstName)
// over there.
OnPropertyChanged(nameof(FirstName));
OnPropertyChanged(nameof(FullName));
}
}

现在 UI 知道这些属性何时发生变化,但主视图模型仍然没有。但是现在我们收到了来自 Person 的通知,这是可以解决的。我们必须再次重写 NewPerson:
private PersonViewModel _newPerson = null;
public PersonViewModel NewPerson
{
get { return _newPerson; }
set
{
if (value != _newPerson)
{
// Take the handler off the old NewPerson, if any.
if (_newPerson != null)
{
_newPerson.PropertyChanged -= NewPerson_PropertyChanged;
}
_newPerson = value;
if (_newPerson != null)
{
_newPerson.PropertyChanged += NewPerson_PropertyChanged;
}
OnPropertyChanged(nameof(NewPerson));
OnPropertyChanged(nameof(AddPersonCanExecute));

// I don't know what your RelayCommand class looks like, but it should
// provide some way to force it to raise its CanExecuteChanged event.
// That's what the Button is waiting for to enable or disable itself.
_addPerson.RaiseCanExecuteChanged()
}
}
}

private void NewPerson_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(Person.FirstName):
case nameof(Person.LastName):
OnPropertyChanged(nameof(AddPersonCanExecute));
AddPerson
break;
}
}

另一点:不要让你的 View 模型成为资源。它不会破坏你的代码,但它没有任何作用,并为你创造了额外的工作。
<Window.DataContext>
<ViewModel:PersonViewModel />
</Window.DataContext>
<Window.Resources>
<!-- remove it from here -->
</Window.Resources>

现在对于属于 Window 本身的所有控件,您的所有绑定(bind)都可以如下所示:
<TextBox 
Text="{Binding NewPerson.FirstName, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="2"
HorizontalAlignment="Left"
Grid.Column="2"
Height="40" Width="200" Margin="10 5"
/>

摆脱 TextBox.Text 上的 Mode=TwoWay;默认情况下,该属性将导致其上的绑定(bind)为双向。保留 UpdateSourceTrigger=PropertyChanged仅在 TextBox.Text 上:这将导致文本框在每次击键时更新 View 模型,而不是仅在文本框失去焦点时更新 View 模型属性的默认行为。你不需要 UpdateSourceTrigger=PropertyChanged在命令绑定(bind)或 Label.Content 绑定(bind)上,因为这些属性永远无法更新 viewmodel 属性。默认情况下,它们是 OneWay,而且它们的工作性质也是如此。

关于c# - WPF MVVM : Update label depending on Textbox input (2nd Attempt),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58304844/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com