DataGridComboBoxColumn 的 IValueConverter 错误
IValueConverter error for DataGridComboBoxColumn
所以我有一个 DataGridComboBoxColumn
ColCID
其值取决于 ColSID
行的另一个单元格(见下面的代码)。我尝试使用 IValueConverter 来实现它,但出现此错误:
Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:(no path); DataItem=null; target element is 'DataGridComboBoxColumn' (HashCode=18018639); target property is 'ItemsSource' (type 'IEnumerable')
XAML:
<DataGridComboBoxColumn x:Name="ColSID" Header="Guild"
SelectedValueBinding="{Binding SID, Mode=TwoWay}"
SelectedValuePath="SID"
DisplayMemberPath="Name" />
<DataGridComboBoxColumn x:Name="ColCID" Header="Channel"
ItemsSource="{ Binding ElementName=ColSID, Converter={StaticResource ChannelConverter} }"
SelectedValueBinding="{Binding CID, Mode=TwoWay}"
SelectedValuePath="CID"
DisplayMemberPath="Name" />
转换器:
public class ChannelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Guild guild = SenderView.Guilds.Find(g => g.SID == value.ToString());
if (guild != null) return guild.Channels;
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
...
public class Guild
{
public class Channel
{
public string CID { get; set; }
public string Name { get; set; }
}
public string SID { get; set; }
public string Name { get; set; }
public List<Channel> Channels { get; set; }
}
在我看来,对于网格中的每一行,您希望用户能够 select Guild
,Channel
Guild
.每个 Guild
都有自己的 collection 个 Channel
,所以我们将从那个 collection 到 select。我要绑定 Channel 和 Guild object,而不仅仅是他们的 ID。如果您愿意,我们可以采用其他方式,但我发现让 ComboBox 查找 object 比我的其余代码必须这样做更容易。当我需要时,SID 或 CID 就在 selected object 上。
以下是您的操作方法。您不能按照惯例将任何内容绑定到 XAML 中的 DataGridComboBoxColumn.ItemsSource
。涉及 属性 的任何解决方案充其量都将有点奇特,但完全没有必要:您可以通过 ElementStyle 和 EditingElementStyle 绑定 ComboBox 的 ItemsSource。 SelectedValuePath
和 SelectedValueBinding
确实有效,但我没有使用它们。
我为 parent 视图模型编写了一个快速 stand-in,它拥有 collection 的公会,用户可以从中 select。请注意,我还将您的 Channel
class 移出了 Guild
。我这样做的唯一原因是我可以在 Guild
class 上为 selected 频道设置 Channel
属性。如果您更愿意将 Channel
保留在原来的位置,只需将 Guild
的 Channel
属性 重命名为 SelectedChannel
或类似名称,然后更改绑定在 XAML 相应地。
"framework mentor" 废话是因为列不是可视化树中的 child 控件。它们是对 DataGrid 创建列 header 以及每行中的单元格的指令。那些 header 和单元格,以及它们的模板化内容,都在可视化树中。 SelectedItemBinding
不是对列的绑定;这是一个绑定,列创建代码将 设置在 ComboBox 的 SelectedItem 属性 上,它最终将在单元格内容中创建。但是 ItemsSource
属性 只是列本身的 属性 。您可以绑定它 with a binding proxy,人们也这样做,但绑定代理是懦夫的出路。
XAML
<DataGrid
ItemsSource="{Binding Selections}"
AutoGenerateColumns="False"
Grid.Row="1"
>
<DataGrid.Resources>
<Style TargetType="ComboBox" x:Key="GuildComboStyle">
<Setter
Property="ItemsSource"
Value="{Binding DataContext.Guilds, RelativeSource={RelativeSource AncestorType=DataGrid}}"
/>
</Style>
<Style
TargetType="ComboBox"
x:Key="ChannelComboStyle"
>
<!-- Our DataContext here is a GuildSelection object, so we look at its Guild
property for a collection of Channels to use.
-->
<Setter Property="ItemsSource" Value="{Binding Guild.Channels}" />
<!-- If there's no selected Guild, prompt the user to select one. -->
<Style.Triggers>
<DataTrigger Binding="{Binding Guild}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="IsEditable" Value="True" />
<Setter Property="Text" Value="Please select a Guild" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn
Header="Guild"
SelectedItemBinding="{Binding Guild, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ElementStyle="{StaticResource GuildComboStyle}"
EditingElementStyle="{StaticResource GuildComboStyle}"
Width="200"
/>
<DataGridComboBoxColumn
Header="Channel"
SelectedItemBinding="{Binding Guild.Channel, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ElementStyle="{StaticResource ChannelComboStyle}"
EditingElementStyle="{StaticResource ChannelComboStyle}"
Width="200"
/>
<DataGridTextColumn Binding="{Binding Guild.SID}" Header="Guild SID" />
<DataGridTextColumn Binding="{Binding Guild.Name}" Header="Guild Name" />
<DataGridTextColumn Binding="{Binding Guild.Channel.CID}" Header="Channel CID" />
<DataGridTextColumn Binding="{Binding Guild.Channel.Name}" Header="Channel Name" />
</DataGrid.Columns>
</DataGrid>
C#
public class MainViewModel
{
public ObservableCollection<GuildSelection> Selections { get; set; }
public ObservableCollection<Guild> Guilds { get; set; }
}
public class GuildSelection : ViewModelBase
{
#region Guild Property
private Guild _guild = null;
public Guild Guild
{
get { return _guild; }
set
{
if (value != _guild)
{
_guild = value;
OnPropertyChanged();
}
}
}
#endregion Guild Property
}
public class Channel
{
public string CID { get; set; }
public string Name { get; set; }
}
public class Guild : ViewModelBase
{
public string SID { get; set; }
public string Name { get; set; }
public List<Channel> Channels { get; set; }
#region Channel Property
private Channel _channel = null;
public Channel Channel
{
get { return _channel; }
set
{
if (value != _channel)
{
_channel = value;
OnPropertyChanged();
}
}
}
#endregion Channel Property
}
#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[System.Runtime.CompilerServices.CallerMemberName] string propName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
#endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class
尝试使用 ElementStyle
并绑定到 SID
属性,当您 select ColSID
中的项目时设置:
<DataGridComboBoxColumn x:Name="ColSID" Header="Guild"
SelectedValueBinding="{Binding SID, Mode=TwoWay}"
SelectedValuePath="SID"
DisplayMemberPath="Name" />
<DataGridComboBoxColumn x:Name="ColCID" Header="Channel"
SelectedValueBinding="{Binding CID, Mode=TwoWay}"
SelectedValuePath="CID"
DisplayMemberPath="Name">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding SID, Converter={StaticResource ChannelConverter}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding SID, Converter={StaticResource ChannelConverter}}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
所以我有一个 DataGridComboBoxColumn
ColCID
其值取决于 ColSID
行的另一个单元格(见下面的代码)。我尝试使用 IValueConverter 来实现它,但出现此错误:
Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:(no path); DataItem=null; target element is 'DataGridComboBoxColumn' (HashCode=18018639); target property is 'ItemsSource' (type 'IEnumerable')
XAML:
<DataGridComboBoxColumn x:Name="ColSID" Header="Guild"
SelectedValueBinding="{Binding SID, Mode=TwoWay}"
SelectedValuePath="SID"
DisplayMemberPath="Name" />
<DataGridComboBoxColumn x:Name="ColCID" Header="Channel"
ItemsSource="{ Binding ElementName=ColSID, Converter={StaticResource ChannelConverter} }"
SelectedValueBinding="{Binding CID, Mode=TwoWay}"
SelectedValuePath="CID"
DisplayMemberPath="Name" />
转换器:
public class ChannelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Guild guild = SenderView.Guilds.Find(g => g.SID == value.ToString());
if (guild != null) return guild.Channels;
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
...
public class Guild
{
public class Channel
{
public string CID { get; set; }
public string Name { get; set; }
}
public string SID { get; set; }
public string Name { get; set; }
public List<Channel> Channels { get; set; }
}
在我看来,对于网格中的每一行,您希望用户能够 select Guild
,Channel
Guild
.每个 Guild
都有自己的 collection 个 Channel
,所以我们将从那个 collection 到 select。我要绑定 Channel 和 Guild object,而不仅仅是他们的 ID。如果您愿意,我们可以采用其他方式,但我发现让 ComboBox 查找 object 比我的其余代码必须这样做更容易。当我需要时,SID 或 CID 就在 selected object 上。
以下是您的操作方法。您不能按照惯例将任何内容绑定到 XAML 中的 DataGridComboBoxColumn.ItemsSource
。涉及 属性 的任何解决方案充其量都将有点奇特,但完全没有必要:您可以通过 ElementStyle 和 EditingElementStyle 绑定 ComboBox 的 ItemsSource。 SelectedValuePath
和 SelectedValueBinding
确实有效,但我没有使用它们。
我为 parent 视图模型编写了一个快速 stand-in,它拥有 collection 的公会,用户可以从中 select。请注意,我还将您的 Channel
class 移出了 Guild
。我这样做的唯一原因是我可以在 Guild
class 上为 selected 频道设置 Channel
属性。如果您更愿意将 Channel
保留在原来的位置,只需将 Guild
的 Channel
属性 重命名为 SelectedChannel
或类似名称,然后更改绑定在 XAML 相应地。
"framework mentor" 废话是因为列不是可视化树中的 child 控件。它们是对 DataGrid 创建列 header 以及每行中的单元格的指令。那些 header 和单元格,以及它们的模板化内容,都在可视化树中。 SelectedItemBinding
不是对列的绑定;这是一个绑定,列创建代码将 设置在 ComboBox 的 SelectedItem 属性 上,它最终将在单元格内容中创建。但是 ItemsSource
属性 只是列本身的 属性 。您可以绑定它 with a binding proxy,人们也这样做,但绑定代理是懦夫的出路。
XAML
<DataGrid
ItemsSource="{Binding Selections}"
AutoGenerateColumns="False"
Grid.Row="1"
>
<DataGrid.Resources>
<Style TargetType="ComboBox" x:Key="GuildComboStyle">
<Setter
Property="ItemsSource"
Value="{Binding DataContext.Guilds, RelativeSource={RelativeSource AncestorType=DataGrid}}"
/>
</Style>
<Style
TargetType="ComboBox"
x:Key="ChannelComboStyle"
>
<!-- Our DataContext here is a GuildSelection object, so we look at its Guild
property for a collection of Channels to use.
-->
<Setter Property="ItemsSource" Value="{Binding Guild.Channels}" />
<!-- If there's no selected Guild, prompt the user to select one. -->
<Style.Triggers>
<DataTrigger Binding="{Binding Guild}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="IsEditable" Value="True" />
<Setter Property="Text" Value="Please select a Guild" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn
Header="Guild"
SelectedItemBinding="{Binding Guild, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ElementStyle="{StaticResource GuildComboStyle}"
EditingElementStyle="{StaticResource GuildComboStyle}"
Width="200"
/>
<DataGridComboBoxColumn
Header="Channel"
SelectedItemBinding="{Binding Guild.Channel, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ElementStyle="{StaticResource ChannelComboStyle}"
EditingElementStyle="{StaticResource ChannelComboStyle}"
Width="200"
/>
<DataGridTextColumn Binding="{Binding Guild.SID}" Header="Guild SID" />
<DataGridTextColumn Binding="{Binding Guild.Name}" Header="Guild Name" />
<DataGridTextColumn Binding="{Binding Guild.Channel.CID}" Header="Channel CID" />
<DataGridTextColumn Binding="{Binding Guild.Channel.Name}" Header="Channel Name" />
</DataGrid.Columns>
</DataGrid>
C#
public class MainViewModel
{
public ObservableCollection<GuildSelection> Selections { get; set; }
public ObservableCollection<Guild> Guilds { get; set; }
}
public class GuildSelection : ViewModelBase
{
#region Guild Property
private Guild _guild = null;
public Guild Guild
{
get { return _guild; }
set
{
if (value != _guild)
{
_guild = value;
OnPropertyChanged();
}
}
}
#endregion Guild Property
}
public class Channel
{
public string CID { get; set; }
public string Name { get; set; }
}
public class Guild : ViewModelBase
{
public string SID { get; set; }
public string Name { get; set; }
public List<Channel> Channels { get; set; }
#region Channel Property
private Channel _channel = null;
public Channel Channel
{
get { return _channel; }
set
{
if (value != _channel)
{
_channel = value;
OnPropertyChanged();
}
}
}
#endregion Channel Property
}
#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[System.Runtime.CompilerServices.CallerMemberName] string propName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
#endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class
尝试使用 ElementStyle
并绑定到 SID
属性,当您 select ColSID
中的项目时设置:
<DataGridComboBoxColumn x:Name="ColSID" Header="Guild"
SelectedValueBinding="{Binding SID, Mode=TwoWay}"
SelectedValuePath="SID"
DisplayMemberPath="Name" />
<DataGridComboBoxColumn x:Name="ColCID" Header="Channel"
SelectedValueBinding="{Binding CID, Mode=TwoWay}"
SelectedValuePath="CID"
DisplayMemberPath="Name">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding SID, Converter={StaticResource ChannelConverter}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding SID, Converter={StaticResource ChannelConverter}}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>