所以这个我很好奇,因为如果我无法正确获取数据,我可能需要更改我的代码库。我希望WPF的绑定专家有类似的东西并且知道如何去做。我正在按照本指南http://wpfthoughts.blogspot.com/2015/04/cannot-find-governing-frameworkelement.html,将datagrid中显示的列表中的值绑定到组合框。如果对象集合中的属性是基本类型,则效果很好。如果它复杂而不是那么多。我还希望它在更改实现INotifyPropertyChanged时更新属性。
随意下载源代码以便于参考:https://github.com/djangojazz/ComboBoxInDataGridViewWPF
BaseViewModel(仅适用于INotifyPropertyChanged重用):
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
基本上我有这样的模型:
public class Type
{
public Type(int typeId, string typeName)
{
TypeId = typeId;
TypeName = typeName;
}
public int TypeId { get; set; }
public string TypeName { get; set; }
}
public class TransactionSimple : BaseViewModel
{
public TransactionSimple(int transactionId, string description, int typeId, decimal amount)
{
TransactionId = transactionId;
Description = description;
TypeId = typeId;
Amount = amount;
}
public int TransactionId { get; set; }
public string Description { get; set; }
private int _typeId;
public int TypeId
{
get { return _typeId; }
set
{
_typeId = value;
OnPropertyChanged(nameof(TypeId));
}
}
public decimal Amount { get; set; }
}
public class TransactionComplex : BaseViewModel
{
public TransactionComplex(int transactionId, string description, int typeId, string typeName, decimal amount)
{
TransactionId = transactionId;
Description = description;
Type = new Type(typeId, typeName);
Amount = amount;
}
public int TransactionId { get; set; }
public string Description { get; set; }
private Type _type;
public Type Type
{
get { return _type; }
set
{
if(_type != null) { MessageBox.Show($"Change to {value.TypeName}"); }
_type = value;
OnPropertyChanged(nameof(Type));
}
}
public decimal Amount { get; set; }
}
ViewModel:
public sealed class MainWindowViewModel : BaseViewModel
{
private ObservableCollection<TransactionSimple> _simples;
private ObservableCollection<TransactionComplex> _complexes;
public MainWindowViewModel()
{
FakeRepo();
}
private ReadOnlyCollection<Type> _types;
public ReadOnlyCollection<Type> Types
{
get => (_types != null) ? _types : _types = new ReadOnlyCollection<Type>(new List<Type> { new Type(1, "Credit"), new Type(2, "Debit") });
}
public ObservableCollection<TransactionSimple> Simples
{
get { return _simples; }
set
{
_simples = value;
OnPropertyChanged(nameof(Simples));
}
}
public ObservableCollection<TransactionComplex> Complexes
{
get { return _complexes; }
set
{
_complexes = value;
OnPropertyChanged(nameof(Complexes));
}
}
private void FakeRepo()
{
var data = new List<TransactionComplex>
{
new TransactionComplex(1, "Got some money", 1, "Credit", 1000m),
new TransactionComplex(2, "spent some money", 2, "Debit", 100m),
new TransactionComplex(3, "spent some more money", 2, "Debit", 300m)
};
Complexes = new ObservableCollection<TransactionComplex>(data);
Simples = new ObservableCollection<TransactionSimple>(data.Select(x => new TransactionSimple(x.TransactionId, x.Description, x.Type.TypeId, x.Amount)));
}
}
更新美国太平洋标准时间下午2:24 最后视图(几乎正常工作):
<Window x:Class="ComboBoxInDataGridViewWPF.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:local="clr-namespace:ComboBoxInDataGridViewWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="Types" Source="{Binding Types}"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Content="SimpleExample" />
<DataGrid Grid.Row="1" ItemsSource="{Binding Simples}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="TransactionId" Binding="{Binding TransactionId}" />
<DataGridTextColumn Header="Description" Binding="{Binding Description}" />
<DataGridComboBoxColumn Header="Type" ItemsSource="{Binding Source={StaticResource Types}}" DisplayMemberPath="TypeName" SelectedValuePath="TypeId" SelectedValueBinding="{Binding Path=TypeId}" />
<DataGridTextColumn Header="Amount" Binding="{Binding Amount}" />
</DataGrid.Columns>
</DataGrid>
<Border Grid.Row="2" Height="50" Background="Black" />
<Label Content="ComplexObjectExample" Grid.Row="3" />
<DataGrid Grid.Row="4" ItemsSource="{Binding Complexes}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="TransactionId" Binding="{Binding TransactionId}" />
<DataGridTextColumn Header="Description" Binding="{Binding Description}" />
<!--This one works for the displays but not for the updates
<DataGridTemplateColumn Header="Type">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource Types}}" DisplayMemberPath="TypeName" SelectedItem="{Binding Type, Mode=TwoWay}" SelectedValue="{Binding Type.TypeId}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type.TypeName}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>-->
<!--This one works but the initial displays are wrong. This seems to be the closest to what I want-->
<DataGridComboBoxColumn Header="Type" SelectedItemBinding="{Binding Type}" >
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Types}"/>
<Setter Property="DisplayMemberPath" Value="TypeName" />
<Setter Property="SelectedItem" Value="{Binding Type}" />
<Setter Property="IsReadOnly" Value="True"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Types}"/>
<Setter Property="DisplayMemberPath" Value="TypeName" />
<Setter Property="SelectedItem" Value="{Binding Type}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<!--This one does not work at all
<DataGridTemplateColumn Header="Type">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Types,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
DisplayMemberPath="TypeName" SelectedItem="{Binding Type}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>-->
<DataGridTextColumn Header="Amount" Binding="{Binding Amount}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
我显然可以将项目绑定到ComboBox,我已经看到了添加Observable Collections(未显示)并引发了复杂类型被调用的属性。但无论我尝试什么,它都不会显示出来。尝试使用Type.TypeName这样的属性或具有不同组合的属性不起作用。有什么想法吗?
答案 0 :(得分:3)
这种荒谬的行为众所周知。由于DataGridColumn
不在视觉树中,因此使用DataGridComboBoxColumn
绑定来自父项目的项目的经典方式无效。
相反,您可以在DataGridTemplateColumn
内创建ComboBox
。这应该几乎以同样的方式解决你的问题。如果要绑定TypeId
此代码有效:
<DataGridTemplateColumn Header="Type">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=DataContext.Types,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
DisplayMemberPath="TypeName"
SelectedValuePath="TypeId"
SelectedValue="{Binding Path=TypeId, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
可以通过将Type
更改为:
ComboBox
<ComboBox ItemsSource="{Binding Path=DataContext.Types,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
DisplayMemberPath="TypeName"
SelectedItem="{Binding Path=Type, UpdateSourceTrigger=PropertyChanged}"/>
或者,您可以查看at this question,其中描述了其他可能的解决方案。