如果愿意,请耐心等待,我是 WPF MVVM 开发的新手。
我正在处理的实用程序(WPF 客户端应用程序)包含一个 4 列数据网格,它在指定的时间段内向用户显示当前可用的数据。返回的记录可能包含完成记录所需的全部或部分数据。用户应该能够修改缺失的部分或完全添加新行以填充缺失的记录。4 列中的 3 列包含只有有效选项的组合框。组合框是级联的,因此第一个组合确定组合 2 和 3 的内容。第 4 列是简单的价格列。
我已经设法让网格与默认返回数据完美配合。组合框正确填充,并将当前记录的值显示为组合中的选定项。
我的问题是添加新行。我一直在努力完成这个内联。这意味着用户单击列表底部的空白行或使用标签,然后将新行添加到集合中接受输入。第一个组合填充在空行中,但选择一个值不会将更改级联到其他 2 个组合。不过,它在填充的行中起作用。
有道理?我可能错过了一些如此愚蠢的简单事情,以至于当有人指出时我会踢自己,但现在我正在撕掉我剩下的小头发。
那么,是否可以让新用户通过跳到下一行来添加一行?我需要什么特别的东西来连接新行中的级联组合框吗?
谢谢!
这是我的 XAML(剥离后基本上是伪代码,所以不要介意拼写错误):
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
d:DesignHeight="768"
d:DesignWidth="1024">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<StackPanel>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Prices}"
SelectedItem="{Binding SelectedPrice}"
CanUserAddRows="True">
<DataGrid.Columns>
<!-- Price Group -->
<DataGridTemplateColumn MinWidth="120"
Header="Price Group">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="cboPriceGroup"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.PriceGroups}"
DisplayMemberPath="Name"
SelectedItem="{Binding PriceGroupObject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- Option 1 -->
<DataGridTemplateColumn MinWidth="120"
Header="Option1">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="cboOption1"
ItemsSource="{Binding PriceGroupObject.Options1}"
DisplayMemberPath="Code"
SelectedValuePath="Id"
SelectedValue="{Binding Option1Id, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- Option 2 -->
<DataGridTemplateColumn MinWidth="120"
Header="Option2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="cboOption2"
ItemsSource="{Binding PriceGroupObject.Options2}"
DisplayMemberPath="Code"
SelectedValuePath="Id"
SelectedValue="{Binding Option2Id, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- Price -->
<DataGridTemplateColumn MinWidth="120"
Header="Price">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="txtPrice"
Text="{Binding Price, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
这是 ViewModel:
// ViewModelBase implements INotifyPropertyChanged
public class MainWindowViewModel : ViewModelBase
{
#region Declarations
// Comes From Backend.Services
private IPriceListService _PriceListService;
private ObservableCollection<Price> _Prices;
private Price _SelectedPrice;
private IEnumerable<PriceGroup> _PriceGroups;
private PriceGroup _SelectedPriceGroup;
// Same for Options1, Options2
#endregion
#region Properties
public ObservableCollection<Price> Prices
{
get { return _Prices; }
set
{
if (_Prices != value)
{
_Prices = value;
OnPropertyChanged("Prices");
}
}
}
public Price SelectedPrice
{
get { return _Price; }
set
{
if (_Price != value)
{
_Price = value;
OnPropertyChanged("SelectedPrice");
}
}
}
// Same for PriceGroups, Options1, Options2
#endregion
#region Constructor
public MainViewModel()
: this(new FakePriceListService())
{
}
// Constructor
public MainViewModel(IPriceListService priceListService)
{
_PriceListService = priceListService;
InitializeViewModel();
}
protected override void InitializeViewModel()
{
// Test ID
LoadFuturesPrices(12345);
LoadPriceGroups();
base.InitializeViewModel();
}
#endregion
#region Methods
void LoadFuturesPrices(short FinancialPeriodId)
{
Prices = _PriceListService.GetFuturesPriceList(FinancialPeriodId);
}
void LoadPriceGroups()
{
PriceGroups = _PriceListService.GetPriceGroups();
}
void PriceGroupSelected(string PriceGroupId)
{
SelectedPriceGroup = (from p in PriceGroups where p.Name == PriceGroupId select p).SingleOrDefault();
}
#endregion
}