将 TextBox 中的用户输入绑定到对象集合,并通过 MVVM 中的 ComboBox 进行填充
Bind User Input from TextBoxes to a Collection of Objects and Populate via ComboBox in MVVM
我对 WPF 和 MVVM 还很陌生,但到目前为止我还没有找到适用于这两种方式绑定方案的方法。我还应该提到,这不是一个学校项目,而是我自己对学习 MVVM 不同方面的兴趣,为了我自己的利益。
我的视图包含一个 Window,带有收集用户输入(SchoolName、SchoolAddress、SchoolTelephone 等)的文本框。该视图还有一个添加按钮,该按钮应保存来自文本框的输入,创建一个学校对象,然后填充一个组合框(带有学校名称)。因此,每次用户单击“添加”按钮时,组合框列表中都会出现一所新学校,并且所有文本框都会重置并准备好输入新学校。我希望保留的另一个条件是,您可以通过组合框中的 selected 项目对文本框进行两种方式的绑定。因此,当您 select 列表中的一所学校时,文本框会填充其对象的信息,并且可以进一步编辑。
我设法得到的是我已将我的 TextBox 绑定到属性,我已将我的 ComboBox ItemsSource 与 ObservableCollection of Schools 绑定,SelectedItem 已绑定到 SelectedSchool 属性 并且我已经将我的添加按钮绑定到 AddSchoolCommand,因此集合确实得到填充。
我无法完成的是找到一种绑定 SelectedSchool 属性 的方法,这样我就可以通过 ComboBox 项目列表修改现有的 School 对象,以及在每次添加新学校时以某种方式从清除的 TextBox 开始。文本框既用作输入收集器,又用作现有学校数据的显示。
所以这是我的方法:
// ViewModelBase
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
// ViewModel:
public ObservableCollection<School> Schools { get; set; }
public RelayCommand AddSchoolCommand { get; set; }
string _SchoolName;
public string SchoolName
{
get
{
return _SchoolName;
}
set
{
if (_SchoolName != value)
{
_SchoolName = value;
RaisePropertyChanged("SchoolName");
}
}
}
string _SchoolAddress;
public string SchoolAddress
{
get
{
return _SchoolName;
}
set
{
if (_SchoolAddress != value)
{
_SchoolAddress = value;
RaisePropertyChanged("SchoolAddress");
}
}
}
string _SchooTelephone;
public string SchooTelephone
{
get
{
return _SchooTelephone;
}
set
{
if (_SchooTelephone != value)
{
_SchoolAddress = value;
RaisePropertyChanged("SchooTelephone");
}
}
}
object _SelectedSchool;
public object SelectedSchool
{
get
{
return _SelectedSchool;
}
set
{
if (_SelectedSchool != value)
{
_SelectedSchool = value;
RaisePropertyChanged("SelectedSchool");
}
}
}
public ViewModelResume()
{
Schools = new ObservableCollection<School>();
AddSchoolCommand = new RelayCommand(AddSchool);
}
void AddSchool(Object obj)
{
Schools.Add(new School
{
Name = SchoolName,
Address = SchoolAddress,
Telephone = SchoolTelephone,
});
}
// XAML
<Grid Width="358" Height="135">
<TextBox x:Name="schoolName" Controls:TextBoxHelper.Watermark="School Name" Controls:TextBoxHelper.ClearTextButton="True" HorizontalAlignment="Left" Height="26" Margin="10,9,0,0" VerticalAlignment="Top" Width="166" Text="{Binding SchoolName, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="schoolAddress" Controls:TextBoxHelper.Watermark="School Address" Controls:TextBoxHelper.ClearTextButton="True" Height="26" Margin="0,9,10,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="167" Text="{Binding School Address, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="schoolTelephone" Controls:TextBoxHelper.Watermark="School Telephone" Controls:TextBoxHelper.ClearTextButton="True" HorizontalAlignment="Left" Height="26" Margin="10,79,0,0" VerticalAlignment="Top" Width="133" Text="{Binding SchoolTelephone, UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox x:Name="schoolsList" Text="Schools List" HorizontalAlignment="Left" Margin="10,155,0,0" VerticalAlignment="Top" Width="268" ToolTip="List of attended schools" IsEditable="True" IsReadOnly="True" ItemsSource="{Binding Schools}" SelectedItem="{Binding SelectedSchool}" DisplayMemberPath="SchoolName" />
<Button x:Name="saveSchoolButton" Content="Add" Margin="318,155,10,0" VerticalAlignment="Top" Height="26" Command="{Binding AddSchoolCommand}"/>
</Grid>
@Muds 的回答可能是做你想做的最好的方法。我在这里留下我的答案只是为了说明另一种方法。
首先,您应该将 SelectedSchool
属性 的类型更改为 School
,而不是对象。然后,在其 属性 setter 中,根据选择设置 SchoolName
、SchoolAddress
和 SchooTelephone
(打字错误?)属性:
SchoolName = _SelectedSchool.Name;
// etc...
这意味着当您在组合框中更改选择时,所选学校的详细信息将转移到文本框中。
在您的 AddSchool
方法中,将新学校添加到列表后,只需清除绑定到文本框的属性:
SchoolName = String.Empty;
// etc...
如果不做一些额外的工作,您使用同一个按钮来添加和编辑学校详细信息的想法是行不通的。每所学校都需要一个唯一的标识符:这样,当您单击 "Add or Edit" 按钮时,您可以查看学校列表以查看该特定学校是否已经存在。如果是,则为编辑,否则为新学校。
您的问题是您没有唯一标识符。您不能使用学校名称,因为这可能会在编辑过程中发生变化。尝试将另一个 属性 添加到您的 School
对象以用作标识符,就像 int
一样(您不必向用户显示此值)。然后,当您创建一所新学校时,创建一个新的唯一 "school ID" 编号并将其分配给该学校。
您的文本框正在侦听视图模型中的属性,这些属性不知道 selectedSchool。
您可以将文本框绑定到 selectedSchool.Properties
首先让你的 SelectedSchool 成为 Type School 并像这样启动它
School _SelectedSchool = new School();
然后更改所有文本框以绑定到选定的学校属性
<TextBox Text="{Binding SelectedSchool.SchoolName, UpdateSourceTrigger=PropertyChanged}"/>
然后当您添加
void AddSchool(Object obj)
{
Schools.Add(SelectedSchool);
SelectedSchool = New School();
}
我对 WPF 和 MVVM 还很陌生,但到目前为止我还没有找到适用于这两种方式绑定方案的方法。我还应该提到,这不是一个学校项目,而是我自己对学习 MVVM 不同方面的兴趣,为了我自己的利益。
我的视图包含一个 Window,带有收集用户输入(SchoolName、SchoolAddress、SchoolTelephone 等)的文本框。该视图还有一个添加按钮,该按钮应保存来自文本框的输入,创建一个学校对象,然后填充一个组合框(带有学校名称)。因此,每次用户单击“添加”按钮时,组合框列表中都会出现一所新学校,并且所有文本框都会重置并准备好输入新学校。我希望保留的另一个条件是,您可以通过组合框中的 selected 项目对文本框进行两种方式的绑定。因此,当您 select 列表中的一所学校时,文本框会填充其对象的信息,并且可以进一步编辑。
我设法得到的是我已将我的 TextBox 绑定到属性,我已将我的 ComboBox ItemsSource 与 ObservableCollection of Schools 绑定,SelectedItem 已绑定到 SelectedSchool 属性 并且我已经将我的添加按钮绑定到 AddSchoolCommand,因此集合确实得到填充。
我无法完成的是找到一种绑定 SelectedSchool 属性 的方法,这样我就可以通过 ComboBox 项目列表修改现有的 School 对象,以及在每次添加新学校时以某种方式从清除的 TextBox 开始。文本框既用作输入收集器,又用作现有学校数据的显示。
所以这是我的方法:
// ViewModelBase
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
// ViewModel:
public ObservableCollection<School> Schools { get; set; }
public RelayCommand AddSchoolCommand { get; set; }
string _SchoolName;
public string SchoolName
{
get
{
return _SchoolName;
}
set
{
if (_SchoolName != value)
{
_SchoolName = value;
RaisePropertyChanged("SchoolName");
}
}
}
string _SchoolAddress;
public string SchoolAddress
{
get
{
return _SchoolName;
}
set
{
if (_SchoolAddress != value)
{
_SchoolAddress = value;
RaisePropertyChanged("SchoolAddress");
}
}
}
string _SchooTelephone;
public string SchooTelephone
{
get
{
return _SchooTelephone;
}
set
{
if (_SchooTelephone != value)
{
_SchoolAddress = value;
RaisePropertyChanged("SchooTelephone");
}
}
}
object _SelectedSchool;
public object SelectedSchool
{
get
{
return _SelectedSchool;
}
set
{
if (_SelectedSchool != value)
{
_SelectedSchool = value;
RaisePropertyChanged("SelectedSchool");
}
}
}
public ViewModelResume()
{
Schools = new ObservableCollection<School>();
AddSchoolCommand = new RelayCommand(AddSchool);
}
void AddSchool(Object obj)
{
Schools.Add(new School
{
Name = SchoolName,
Address = SchoolAddress,
Telephone = SchoolTelephone,
});
}
// XAML
<Grid Width="358" Height="135">
<TextBox x:Name="schoolName" Controls:TextBoxHelper.Watermark="School Name" Controls:TextBoxHelper.ClearTextButton="True" HorizontalAlignment="Left" Height="26" Margin="10,9,0,0" VerticalAlignment="Top" Width="166" Text="{Binding SchoolName, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="schoolAddress" Controls:TextBoxHelper.Watermark="School Address" Controls:TextBoxHelper.ClearTextButton="True" Height="26" Margin="0,9,10,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="167" Text="{Binding School Address, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="schoolTelephone" Controls:TextBoxHelper.Watermark="School Telephone" Controls:TextBoxHelper.ClearTextButton="True" HorizontalAlignment="Left" Height="26" Margin="10,79,0,0" VerticalAlignment="Top" Width="133" Text="{Binding SchoolTelephone, UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox x:Name="schoolsList" Text="Schools List" HorizontalAlignment="Left" Margin="10,155,0,0" VerticalAlignment="Top" Width="268" ToolTip="List of attended schools" IsEditable="True" IsReadOnly="True" ItemsSource="{Binding Schools}" SelectedItem="{Binding SelectedSchool}" DisplayMemberPath="SchoolName" />
<Button x:Name="saveSchoolButton" Content="Add" Margin="318,155,10,0" VerticalAlignment="Top" Height="26" Command="{Binding AddSchoolCommand}"/>
</Grid>
@Muds 的回答可能是做你想做的最好的方法。我在这里留下我的答案只是为了说明另一种方法。
首先,您应该将 SelectedSchool
属性 的类型更改为 School
,而不是对象。然后,在其 属性 setter 中,根据选择设置 SchoolName
、SchoolAddress
和 SchooTelephone
(打字错误?)属性:
SchoolName = _SelectedSchool.Name;
// etc...
这意味着当您在组合框中更改选择时,所选学校的详细信息将转移到文本框中。
在您的 AddSchool
方法中,将新学校添加到列表后,只需清除绑定到文本框的属性:
SchoolName = String.Empty;
// etc...
如果不做一些额外的工作,您使用同一个按钮来添加和编辑学校详细信息的想法是行不通的。每所学校都需要一个唯一的标识符:这样,当您单击 "Add or Edit" 按钮时,您可以查看学校列表以查看该特定学校是否已经存在。如果是,则为编辑,否则为新学校。
您的问题是您没有唯一标识符。您不能使用学校名称,因为这可能会在编辑过程中发生变化。尝试将另一个 属性 添加到您的 School
对象以用作标识符,就像 int
一样(您不必向用户显示此值)。然后,当您创建一所新学校时,创建一个新的唯一 "school ID" 编号并将其分配给该学校。
您的文本框正在侦听视图模型中的属性,这些属性不知道 selectedSchool。 您可以将文本框绑定到 selectedSchool.Properties
首先让你的 SelectedSchool 成为 Type School 并像这样启动它
School _SelectedSchool = new School();
然后更改所有文本框以绑定到选定的学校属性
<TextBox Text="{Binding SelectedSchool.SchoolName, UpdateSourceTrigger=PropertyChanged}"/>
然后当您添加
void AddSchool(Object obj)
{
Schools.Add(SelectedSchool);
SelectedSchool = New School();
}