使用自定义依赖属性的数据绑定失败
DataBinding Failure using Custom Dependency Properties
我想根据我认为的某些 TextBox
中的搜索文本突出显示 DataGrid
中的所有单元格。为此,我有以下静态 class 和所需的依赖属性 (DP)
public static class DataGridTextSearch
{
public static readonly DependencyProperty SearchValueProperty =
DependencyProperty.RegisterAttached("SearchValue", typeof(string), typeof(DataGridTextSearch),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Inherits));
public static string GetSearchValue(DependencyObject obj) {
return (string)obj.GetValue(SearchValueProperty);
}
public static void SetSearchValue(DependencyObject obj, string value) {
obj.SetValue(SearchValueProperty, value);
}
public static readonly DependencyProperty HasTextMatchProperty =
DependencyProperty.RegisterAttached("HasTextMatch", typeof(bool),
typeof(DataGridTextSearch), new UIPropertyMetadata(false));
public static bool GetHasTextMatch(DependencyObject obj) {
return (bool)obj.GetValue(HasTextMatchProperty);
}
public static void SetHasTextMatch(DependencyObject obj, bool value) {
obj.SetValue(HasTextMatchProperty, value);
}
}
然后在我的 XAML 我有以下
<UserControl x:Class="GambitFramework.TaurusViewer.Views.TaurusViewerView"
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:Caliburn="http://www.caliburnproject.org"
xmlns:Converters="clr-namespace:GambitFramework.TaurusViewer.Converters"
xmlns:Helpers="clr-namespace:GambitFramework.TaurusViewer.Helpers">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Converters:ULongToDateTimeStringConverter x:Key="ULongToDateTimeStringConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Name="SearchTextBox"
Grid.Row="0"
Margin="5"
Width="250"
VerticalContentAlignment="Center"
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TabControl Grid.Row="1">
<TabItem Header="Events">
<DataGrid x:Name="EventsGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
SelectionUnit="FullRow"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
IsSynchronizedWithCurrentItem="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
Helpers:DataGridTextSearch.HasTextMatch="False"
Helpers:DataGridTextSearch.SearchValue="{Binding ElementName=SearchTextBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
GridLinesVisibility="{Binding GridLinesVisibility}"
SelectedItem="{Binding SelectedEvent, Mode=TwoWay}"
ItemsSource="{Binding EventsCollection}">
<DataGrid.Columns>
<DataGridTextColumn Header="Event ID" IsReadOnly="True" Binding="{Binding event_id}"/>
<DataGridTextColumn Header="Competition" IsReadOnly="True" Binding="{Binding comp}"/>
<DataGridTextColumn Header="Home Team" IsReadOnly="True" Binding="{Binding home}"/>
<DataGridTextColumn Header="Away Team" IsReadOnly="True" Binding="{Binding away}"/>
<DataGridTextColumn Header="Start Time"
IsReadOnly="True"
Binding="{Binding start_time, Converter={StaticResource ULongToDateTimeStringConverter}}"/>
</DataGrid.Columns>
<DataGrid.Resources>
<Converters:SearchValueConverter x:Key="SearchValueConverter"/>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Helpers:DataGridTextSearch.HasTextMatch">
<Setter.Value>
<MultiBinding Converter="{StaticResource SearchValueConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Content.Text"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Helpers:DataGridTextSearch.SearchValue"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Helpers:DataGridTextSearch.HasTextMatch" Value="True">
<Setter Property="Background" Value="Orange"/>
<!--<Setter Property="Background" Value="{DynamicResource HighlightBrush}"/>-->
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
</TabItem>
</TabControl>
</Grid>
</UserControl>
我的 ULongToDateTimeStringConverter
在 Style.xaml
中定义并且有效,而 SearchValueConverter
定义为
public class SearchValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture) {
string cellText = values[0] == null ? string.Empty : values[0].ToString();
string searchText = values[1] as string;
if (!string.IsNullOrEmpty(searchText) && !string.IsNullOrEmpty(cellText))
return cellText.ToLower().StartsWith(searchText.ToLower());
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture) {
return null;
}
}
问题是 SearchValueConverter
转换器似乎只在加载网格时才被调用。我已经使用 Snoop 检查绑定,所有绑定都是绿色且良好的。 Helpers:DataGridTextSearch.SearchValue
在按键时用 Snoop 检查的元素发生变化,但转换器代码永远不会是 used/invoked。我相信这是一个 DataContext
问题,但我不确定如何准确地找出或确实如何解决这个问题。我的 DataContext
是由 Caliburn 以通常的方式设置的。
我在 Snoop 中注意到我得到了
An unhanded exception has occurred on the user interface thread.
Message: Cannot set Expression. It is marked as 'NonShareable' and has already been used.
Stacktrace:
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
...
当我 "Delve Binding Expression" 在 DataGridTextSearch.SearchValue
上。这可能是 Snoop 问题,但我认为这可能与我遇到的问题有关。
我做错了什么?
感谢您的宝贵时间。
据我所知,您的问题似乎出在这个绑定上
<Binding RelativeSource="{RelativeSource Self}" Path="Helpers:DataGridTextSearch.SearchValue"/>
这是绑定到 DataGridCell 上的 AttachedProperty,而不是您想绑定到的是 DataGrid属性 上的附加属性
此绑定应正确绑定到您的 属性
<Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" Path="Helpers:DataGridTextSearch.SearchValue"/>
您还可以限制可以设置附加 属性 的对象,目前每个依赖对象都将接受您的附加 属性,即使您只希望它是 DataGrid。
public static string GetSearchValue(DataGrid obj) {
return (string)obj.GetValue(SearchValueProperty);
}
public static void SetSearchValue(DataGrid obj, string value) {
obj.SetValue(SearchValueProperty, value);
}
像这样更改您的 属性 定义会将 属性 的设置和获取限制为仅 DataGrid,这将导致您当前的绑定中断并报告错误,而不是仅仅接受它,但是没有按照你的期望去做。
在没有测试您的代码的情况下,绑定路径应该是
Path="(Helpers:DataGridTextSearch.SearchValue)"
因为是附件属性。请参阅 MSDN 上的 PropertyPath XAML Syntax。
我想根据我认为的某些 TextBox
中的搜索文本突出显示 DataGrid
中的所有单元格。为此,我有以下静态 class 和所需的依赖属性 (DP)
public static class DataGridTextSearch
{
public static readonly DependencyProperty SearchValueProperty =
DependencyProperty.RegisterAttached("SearchValue", typeof(string), typeof(DataGridTextSearch),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Inherits));
public static string GetSearchValue(DependencyObject obj) {
return (string)obj.GetValue(SearchValueProperty);
}
public static void SetSearchValue(DependencyObject obj, string value) {
obj.SetValue(SearchValueProperty, value);
}
public static readonly DependencyProperty HasTextMatchProperty =
DependencyProperty.RegisterAttached("HasTextMatch", typeof(bool),
typeof(DataGridTextSearch), new UIPropertyMetadata(false));
public static bool GetHasTextMatch(DependencyObject obj) {
return (bool)obj.GetValue(HasTextMatchProperty);
}
public static void SetHasTextMatch(DependencyObject obj, bool value) {
obj.SetValue(HasTextMatchProperty, value);
}
}
然后在我的 XAML 我有以下
<UserControl x:Class="GambitFramework.TaurusViewer.Views.TaurusViewerView"
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:Caliburn="http://www.caliburnproject.org"
xmlns:Converters="clr-namespace:GambitFramework.TaurusViewer.Converters"
xmlns:Helpers="clr-namespace:GambitFramework.TaurusViewer.Helpers">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Converters:ULongToDateTimeStringConverter x:Key="ULongToDateTimeStringConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Name="SearchTextBox"
Grid.Row="0"
Margin="5"
Width="250"
VerticalContentAlignment="Center"
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TabControl Grid.Row="1">
<TabItem Header="Events">
<DataGrid x:Name="EventsGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
SelectionUnit="FullRow"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
IsSynchronizedWithCurrentItem="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
Helpers:DataGridTextSearch.HasTextMatch="False"
Helpers:DataGridTextSearch.SearchValue="{Binding ElementName=SearchTextBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
GridLinesVisibility="{Binding GridLinesVisibility}"
SelectedItem="{Binding SelectedEvent, Mode=TwoWay}"
ItemsSource="{Binding EventsCollection}">
<DataGrid.Columns>
<DataGridTextColumn Header="Event ID" IsReadOnly="True" Binding="{Binding event_id}"/>
<DataGridTextColumn Header="Competition" IsReadOnly="True" Binding="{Binding comp}"/>
<DataGridTextColumn Header="Home Team" IsReadOnly="True" Binding="{Binding home}"/>
<DataGridTextColumn Header="Away Team" IsReadOnly="True" Binding="{Binding away}"/>
<DataGridTextColumn Header="Start Time"
IsReadOnly="True"
Binding="{Binding start_time, Converter={StaticResource ULongToDateTimeStringConverter}}"/>
</DataGrid.Columns>
<DataGrid.Resources>
<Converters:SearchValueConverter x:Key="SearchValueConverter"/>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Helpers:DataGridTextSearch.HasTextMatch">
<Setter.Value>
<MultiBinding Converter="{StaticResource SearchValueConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Content.Text"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Helpers:DataGridTextSearch.SearchValue"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Helpers:DataGridTextSearch.HasTextMatch" Value="True">
<Setter Property="Background" Value="Orange"/>
<!--<Setter Property="Background" Value="{DynamicResource HighlightBrush}"/>-->
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
</TabItem>
</TabControl>
</Grid>
</UserControl>
我的 ULongToDateTimeStringConverter
在 Style.xaml
中定义并且有效,而 SearchValueConverter
定义为
public class SearchValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture) {
string cellText = values[0] == null ? string.Empty : values[0].ToString();
string searchText = values[1] as string;
if (!string.IsNullOrEmpty(searchText) && !string.IsNullOrEmpty(cellText))
return cellText.ToLower().StartsWith(searchText.ToLower());
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture) {
return null;
}
}
问题是 SearchValueConverter
转换器似乎只在加载网格时才被调用。我已经使用 Snoop 检查绑定,所有绑定都是绿色且良好的。 Helpers:DataGridTextSearch.SearchValue
在按键时用 Snoop 检查的元素发生变化,但转换器代码永远不会是 used/invoked。我相信这是一个 DataContext
问题,但我不确定如何准确地找出或确实如何解决这个问题。我的 DataContext
是由 Caliburn 以通常的方式设置的。
我在 Snoop 中注意到我得到了
An unhanded exception has occurred on the user interface thread.
Message: Cannot set Expression. It is marked as 'NonShareable' and has already been used. Stacktrace: at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) ...
当我 "Delve Binding Expression" 在 DataGridTextSearch.SearchValue
上。这可能是 Snoop 问题,但我认为这可能与我遇到的问题有关。
我做错了什么?
感谢您的宝贵时间。
据我所知,您的问题似乎出在这个绑定上
<Binding RelativeSource="{RelativeSource Self}" Path="Helpers:DataGridTextSearch.SearchValue"/>
这是绑定到 DataGridCell 上的 AttachedProperty,而不是您想绑定到的是 DataGrid属性 上的附加属性
此绑定应正确绑定到您的 属性
<Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" Path="Helpers:DataGridTextSearch.SearchValue"/>
您还可以限制可以设置附加 属性 的对象,目前每个依赖对象都将接受您的附加 属性,即使您只希望它是 DataGrid。
public static string GetSearchValue(DataGrid obj) {
return (string)obj.GetValue(SearchValueProperty);
}
public static void SetSearchValue(DataGrid obj, string value) {
obj.SetValue(SearchValueProperty, value);
}
像这样更改您的 属性 定义会将 属性 的设置和获取限制为仅 DataGrid,这将导致您当前的绑定中断并报告错误,而不是仅仅接受它,但是没有按照你的期望去做。
在没有测试您的代码的情况下,绑定路径应该是
Path="(Helpers:DataGridTextSearch.SearchValue)"
因为是附件属性。请参阅 MSDN 上的 PropertyPath XAML Syntax。