使用动态项绑定到Listbox的DataTemplate中的控件DependencyProperty不起作用
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用动态项绑定到Listbox的DataTemplate中的控件DependencyProperty不起作用相关的知识,希望对你有一定的参考价值。
好吧,因为我花了一天时间在stackoverflow中阅读文章而没有成功,我必须寻求帮助。完整示例如下。
简短版本:我有一个DoubleTextBox,它提供了DependencyProperty“Number”。当我直接使用DoubleTextBox时,绑定和验证工作正常。但是,当我在Listbox的DataTemplate中使用它时,ObservableCollection中对象的绑定确实失败。 ValidatesOnDataErrors也不起作用。
非常非常感谢你!
XAML:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel x:Name="ViewModel"/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="5"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Time (s):" Width="60"/>
<local:DoubleTextBox Width="60"
Number="{Binding Time, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" Decimals="2"/>
</StackPanel>
</StackPanel>
</Grid>
<Grid Grid.Column="1" Background="SteelBlue"/>
<Grid Grid.Column="2">
<Grid.Resources>
<DataTemplate x:Key="MethodProgramViewTemplate">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Flow"/>
<local:DoubleTextBox Grid.Column="1" TabIndex="0" Number="{Binding Flow, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Decimals="2" />
</Grid>
</DataTemplate>
</Grid.Resources>
<ListBox x:Name="PumpProgramListBox"
KeyboardNavigation.TabNavigation="Local"
ItemsSource="{Binding Path=MethodProgramView}"
ItemTemplate="{StaticResource MethodProgramViewTemplate}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</Grid>
</Window>
ViewModel:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Data;
namespace WpfApp1
{
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private double time = 0.0;
public double Time
{
get => this.time;
set
{
if (this.time != value)
{
System.Diagnostics.Debug.WriteLine(String.Format("ViewModel Time - from {0} to {1}", this.time, value));
this.time = value;
RaisePropertyChanged(nameof(Time));
}
}
}
public ObservableCollection<Method> methodProgram { get; private set; } = new ObservableCollection<Method>();
private CollectionViewSource methodProgramView;
public ICollectionView MethodProgramView
{
get
{
if (methodProgramView == null)
{
methodProgramView = new CollectionViewSource();
methodProgramView.Source = methodProgram;
}
return methodProgramView.View;
}
}
public ViewModel()
{
this.Time = 1.5;
for (int i = 1; i <= 3; i++)
{
this.methodProgram.Add(new Method() { Flow = i });
}
}
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string propName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
public string Error => "";
public string this[string columnName]
{
get
{
string result = null;
switch (columnName)
{
case nameof(Time):
result = this.Time < 0 ? "Time must be positive" : null;
break;
default:
break;
}
return result;
}
}
}
}
“方法”对象:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace WpfApp1
{
public class Method : INotifyPropertyChanged, IDataErrorInfo
{
private double flow = 0.0;
public double Flow
{
get => this.flow;
set
{
if (this.flow != value)
{
System.Diagnostics.Debug.WriteLine(String.Format("Method Flow - from {0} to {1}", this.flow, value));
this.flow = value;
RaisePropertyChanged(nameof(Flow));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string propName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
public string Error => "";
public string this[string columnName]
{
get
{
string result = null;
switch (columnName)
{
case nameof(Flow):
result = this.Flow < 0 ? "Flow must be positive" : null;
break;
default:
break;
}
return result;
}
}
}
}
DoubleTextBox:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace WpfApp1
{
public class DoubleTextBox : TextBox
{
#region Properties
public double Number
{
get { return (double)this.GetValue(NumberProperty); }
set { this.SetValue(NumberProperty, value); }
}
public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
nameof(Number), typeof(double), typeof(DoubleTextBox),
new FrameworkPropertyMetadata(3.3d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(NumberProperty_PropertyChanged))
);
public int Decimals
{
get { return (int)this.GetValue(DecimalsProperty); }
set { this.SetValue(DecimalsProperty, value); }
}
public static readonly DependencyProperty DecimalsProperty = DependencyProperty.Register(
nameof(Decimals), typeof(int), typeof(DoubleTextBox),
new FrameworkPropertyMetadata((int)2, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(DecimalsProperty_PropertyChanged))
);
#endregion Properties
#region Constructor
public DoubleTextBox()
{
this.TextChanged += DoubleTextBox_TextChanged;
this.LostFocus += DoubleTextBox_LostFocus;
this.PreviewTextInput += DoubleTextBox_PreviewTextInput;
this.RefreshText();
}
#endregion Constructor
#region Methods
private static void NumberProperty_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(String.Format("NumberProperty_PropertyChanged - from {0} to {1}", e.OldValue, e.NewValue));
(d as DoubleTextBox).RefreshText();
}
private static void DecimalsProperty_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as DoubleTextBox).RefreshText();
}
private void RefreshText()
{
System.Diagnostics.Debug.WriteLine(String.Format("DoubleTextBox - DataContext: {0}", this.DataContext));
Text = Number.ToString("N" + this.Decimals.ToString());
}
private void DoubleTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsTextAllowed(e.Text);
}
private static bool IsTextAllowed(string text)
{
string CultureName = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo(CultureName);
char DecimalSeparator = ci.NumberFormat.NumberDecimalSeparator[0];
Regex regex = new Regex(String.Format("([-+0-9]+[{0}][0-9]+)|([-+0-9{0}])", DecimalSeparator)); //regex that matches only numbers
bool match = regex.IsMatch(text);
return regex.IsMatch(text);
}
void DoubleTextBox_LostFocus(object sender, System.Windows.RoutedEventArgs e)
{
RefreshText();
}
void DoubleTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
double number;
if (string.IsNullOrEmpty(Text))
{
Number = 0;
}
else if (double.TryParse(Text, out number))
{
Number = Double.Parse(number.ToString("N" + this.Decimals.ToString()));
}
else
{
BindingExpression bex = GetBindingExpression(NumberProperty);
if (bex != null)
{
ValidationError validationError = new ValidationError(new ExceptionValidationRule(), bex);
validationError.ErrorContent = "Number is not valid";
Validation.MarkInvalid(bex, validationError);
}
}
}
#endregion Methods
}
}
答案
必须删除DoubleTextBox的构造函数中的RefreshText()调用。仍然不知道为什么,但它的工作原理。
感谢@ user1481065和这个问题:WPF Binding to DependencyProperty of UserControl in DataTemplate is not working
以上是关于使用动态项绑定到Listbox的DataTemplate中的控件DependencyProperty不起作用的主要内容,如果未能解决你的问题,请参考以下文章
使用 SelectionMode = Multiple 对 ListBox 进行数据绑定