如何在 wpf mvvm 中将数据网格行与文本框绑定
How to bind a datagrid row with text boxes in wpf mvvm
我有一个包含一些产品详细信息的数据网格,当我双击数据网格中的特定产品时,我需要将产品详细信息加载到一些文本框中。
提前致谢。
欢迎使用 Whosebug!如果没有以下内容,通常人们不会回答问题。
- 关于您尝试执行的操作的清晰摘要。
- 所有相关代码
- 您遇到的任何异常情况
- 您为了到达目的地而使用的所有研究链接
话虽如此,我会给你一个你所要求的演示。
这里是XAML
<Window x:Class="datagriddemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:datagriddemo"
Title="MainWindow" Height="350" Width="420" Background="Gray">
<!--This is where we define the resource that the XAML here is going to
use. As you can see, I am importing our view model-->
<Window.Resources>
<ViewModel:ProductGridViewModel x:Key="ProductViewModel"/>
</Window.Resources>
<!--This is the main layout of the XAML. In the Grid below,
I set the "DataContext" to our static resource we just defined above.
What this does is tell everyone inside this grid, "If you don't define
your own data context, you're going to use mine" Because of this, all
of the elements inside this grid will have access to the public properties
of the ViewModel-->
<Grid DataContext="{StaticResource ResourceKey=ProductViewModel}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<!-- This is the datagrid that we are displaying. The two important things
to note here are "ItemsSource" and "SelectedItem". "ItemsSource" is the collection
that we want to display in our grid. This is where are product models are stored.
SelectedProduct is going to be where the selected grid row is stored so we can
access its data with the text boxes defined below. -->
<DataGrid
Width="500"
Grid.Column="0"
AutoGenerateColumns="False"
ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Header="Product ID" Binding="{Binding ProductID, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn IsReadOnly="True" Header="Product Name" Binding="{Binding ProductName, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn IsReadOnly="True" Header="Total Sold" Binding="{Binding TotalSold, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
<!-- This stack panel contains the text boxes we are going to use to edit our data. Notice that the
bindings point to SelectedProduct.Property. This is because we are accessing properties inside of
the SelectedProduct property in our ViewModel. When we edit these text boxes the data in the grid
will automatically change. -->
<StackPanel Height="100" Background="Wheat" Margin="10" Orientation="Vertical" Grid.Column="1">
<TextBlock FontWeight="Bold" Width="100" TextWrapping="Wrap">Update your product info!</TextBlock>
<TextBox Width="100" Text="{Binding SelectedProduct.ProductName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Width="100" Text="{Binding SelectedProduct.TotalSold, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
接下来是我们的 ViewModel。如果视图是蛋糕上的糖霜,那么您可以将视图模型想象成布丁周围的蛋糕。您的视图模型是您的逻辑所在的地方。它会做分类、处理和其他事情。
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace datagriddemo
{
public class ProductGridViewModel
{
private ProductModel _SelectedProduct;
private ObservableCollection<ProductModel> _Products;
/// <summary>
/// Notice that though this updates the grid as you
/// add or remove elements, it doesn't call onproperty
/// changed in the setter... Magic? Nope, Observable
/// collections call OnPropertyChanged for you.
///
/// Caveat: They will NOT call on property changed
/// for you if you do Products = new ObservableCollection...
/// Only when you do Products.Add(yourProduct)...
/// </summary>
public ObservableCollection<ProductModel> Products
{
get { return _Products; }
set { _Products = value; }
}
/// <summary>
/// This is the selected row in the grid. It automatically changes
/// when you select new rows because we set the grid SelectedItem property
/// to Mode=TwoWay
/// </summary>
public ProductModel SelectedProduct
{
get { return _SelectedProduct; }
set { _SelectedProduct = value; }
}
/// <summary>
/// View Models constructor. It get's called automatically when the view
/// is initialized because we declared it as a static resource in the XAML.
/// </summary>
public ProductGridViewModel()
{
//DONT FORGET TO NEW UP YOUR OBSERVABLE COLLECTIONS!!
Products = new ObservableCollection<ProductModel>();
//Don't forget to generate the data!
GenerateProducts();
}
/// <summary>
/// Use this method to generate dummy data
/// </summary>
private void GenerateProducts()
{
for (int x = 0; x < 100; x++)
{
this.Products.Add(new ProductModel(x,"Product #"+x,x+50));
}
}
}
}
终于有你的模特了。这是您的实际数据。这,是的,这就是你的布丁。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace datagriddemo
{
public class ProductModel : INotifyPropertyChanged
{
private Int32 _ProductID;
private String _ProductName;
private Int32 _TotalSold;
/// <summary>
/// Note for the properties below:
/// Notice that first, the property names match those bound in the XAML
/// This is all part of the grand scheme.
///
/// When the OnProperty changed is called, the UI knows to go search for
/// those properties. It's important that these all have the correct spelling
/// and casing.
/// </summary>
public Int32 TotalSold
{
get { return _TotalSold; }
set
{
_TotalSold = value;
OnPropertyChanged("TotalSold");
}
}
public String ProductName
{
get { return _ProductName; }
set
{
_ProductName = value;
OnPropertyChanged("ProductName");
}
}
public Int32 ProductID
{
get { return _ProductID; }
set
{
_ProductID = value;
OnPropertyChanged("ProductID");
}
}
/// <summary>
/// Just a normal constructor to load up our properties.
/// </summary>
/// <param name="productID"></param>
/// <param name="productName"></param>
/// <param name="totalSold"></param>
public ProductModel(Int32 productID, String productName, Int32 totalSold)
{
this.ProductID = productID;
this.ProductName = productName;
this.TotalSold = totalSold;
}
/// <summary>
/// This is for updating the UI
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// When a property changes in this object, if you want it reflected on the
/// UI you need to call this function
/// </summary>
/// <param name="propertyName"></param>
public void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
终于到了预期的结果
在第一张图片中,我们看到我们的网格加载了新数据
如果您单击一行,您会看到它的数据填充在侧面的两个文本框中。
最后,如果您更改文本框中的数据,您将在网格中看到它立即更新。
就是这样!一个完整的 MVVM 数据网格到文本框和返回解决方案。希望这可以帮助。请记住我所说的作为提问者的期望,祝您玩得开心!欢迎使用 WPF 和 MVVM。
我有一个包含一些产品详细信息的数据网格,当我双击数据网格中的特定产品时,我需要将产品详细信息加载到一些文本框中。
提前致谢。
欢迎使用 Whosebug!如果没有以下内容,通常人们不会回答问题。
- 关于您尝试执行的操作的清晰摘要。
- 所有相关代码
- 您遇到的任何异常情况
- 您为了到达目的地而使用的所有研究链接
话虽如此,我会给你一个你所要求的演示。
这里是XAML
<Window x:Class="datagriddemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:datagriddemo"
Title="MainWindow" Height="350" Width="420" Background="Gray">
<!--This is where we define the resource that the XAML here is going to
use. As you can see, I am importing our view model-->
<Window.Resources>
<ViewModel:ProductGridViewModel x:Key="ProductViewModel"/>
</Window.Resources>
<!--This is the main layout of the XAML. In the Grid below,
I set the "DataContext" to our static resource we just defined above.
What this does is tell everyone inside this grid, "If you don't define
your own data context, you're going to use mine" Because of this, all
of the elements inside this grid will have access to the public properties
of the ViewModel-->
<Grid DataContext="{StaticResource ResourceKey=ProductViewModel}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<!-- This is the datagrid that we are displaying. The two important things
to note here are "ItemsSource" and "SelectedItem". "ItemsSource" is the collection
that we want to display in our grid. This is where are product models are stored.
SelectedProduct is going to be where the selected grid row is stored so we can
access its data with the text boxes defined below. -->
<DataGrid
Width="500"
Grid.Column="0"
AutoGenerateColumns="False"
ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Header="Product ID" Binding="{Binding ProductID, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn IsReadOnly="True" Header="Product Name" Binding="{Binding ProductName, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn IsReadOnly="True" Header="Total Sold" Binding="{Binding TotalSold, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
<!-- This stack panel contains the text boxes we are going to use to edit our data. Notice that the
bindings point to SelectedProduct.Property. This is because we are accessing properties inside of
the SelectedProduct property in our ViewModel. When we edit these text boxes the data in the grid
will automatically change. -->
<StackPanel Height="100" Background="Wheat" Margin="10" Orientation="Vertical" Grid.Column="1">
<TextBlock FontWeight="Bold" Width="100" TextWrapping="Wrap">Update your product info!</TextBlock>
<TextBox Width="100" Text="{Binding SelectedProduct.ProductName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Width="100" Text="{Binding SelectedProduct.TotalSold, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
接下来是我们的 ViewModel。如果视图是蛋糕上的糖霜,那么您可以将视图模型想象成布丁周围的蛋糕。您的视图模型是您的逻辑所在的地方。它会做分类、处理和其他事情。
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace datagriddemo
{
public class ProductGridViewModel
{
private ProductModel _SelectedProduct;
private ObservableCollection<ProductModel> _Products;
/// <summary>
/// Notice that though this updates the grid as you
/// add or remove elements, it doesn't call onproperty
/// changed in the setter... Magic? Nope, Observable
/// collections call OnPropertyChanged for you.
///
/// Caveat: They will NOT call on property changed
/// for you if you do Products = new ObservableCollection...
/// Only when you do Products.Add(yourProduct)...
/// </summary>
public ObservableCollection<ProductModel> Products
{
get { return _Products; }
set { _Products = value; }
}
/// <summary>
/// This is the selected row in the grid. It automatically changes
/// when you select new rows because we set the grid SelectedItem property
/// to Mode=TwoWay
/// </summary>
public ProductModel SelectedProduct
{
get { return _SelectedProduct; }
set { _SelectedProduct = value; }
}
/// <summary>
/// View Models constructor. It get's called automatically when the view
/// is initialized because we declared it as a static resource in the XAML.
/// </summary>
public ProductGridViewModel()
{
//DONT FORGET TO NEW UP YOUR OBSERVABLE COLLECTIONS!!
Products = new ObservableCollection<ProductModel>();
//Don't forget to generate the data!
GenerateProducts();
}
/// <summary>
/// Use this method to generate dummy data
/// </summary>
private void GenerateProducts()
{
for (int x = 0; x < 100; x++)
{
this.Products.Add(new ProductModel(x,"Product #"+x,x+50));
}
}
}
}
终于有你的模特了。这是您的实际数据。这,是的,这就是你的布丁。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace datagriddemo
{
public class ProductModel : INotifyPropertyChanged
{
private Int32 _ProductID;
private String _ProductName;
private Int32 _TotalSold;
/// <summary>
/// Note for the properties below:
/// Notice that first, the property names match those bound in the XAML
/// This is all part of the grand scheme.
///
/// When the OnProperty changed is called, the UI knows to go search for
/// those properties. It's important that these all have the correct spelling
/// and casing.
/// </summary>
public Int32 TotalSold
{
get { return _TotalSold; }
set
{
_TotalSold = value;
OnPropertyChanged("TotalSold");
}
}
public String ProductName
{
get { return _ProductName; }
set
{
_ProductName = value;
OnPropertyChanged("ProductName");
}
}
public Int32 ProductID
{
get { return _ProductID; }
set
{
_ProductID = value;
OnPropertyChanged("ProductID");
}
}
/// <summary>
/// Just a normal constructor to load up our properties.
/// </summary>
/// <param name="productID"></param>
/// <param name="productName"></param>
/// <param name="totalSold"></param>
public ProductModel(Int32 productID, String productName, Int32 totalSold)
{
this.ProductID = productID;
this.ProductName = productName;
this.TotalSold = totalSold;
}
/// <summary>
/// This is for updating the UI
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// When a property changes in this object, if you want it reflected on the
/// UI you need to call this function
/// </summary>
/// <param name="propertyName"></param>
public void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
终于到了预期的结果
在第一张图片中,我们看到我们的网格加载了新数据
如果您单击一行,您会看到它的数据填充在侧面的两个文本框中。
最后,如果您更改文本框中的数据,您将在网格中看到它立即更新。
就是这样!一个完整的 MVVM 数据网格到文本框和返回解决方案。希望这可以帮助。请记住我所说的作为提问者的期望,祝您玩得开心!欢迎使用 WPF 和 MVVM。