Ограничить столбец в списке просмотра больше, чем X (NotifyPropertyChanged не работает)
Вот как моя программа ведет себя, когда я набираю цифры:
У меня есть список, который привязан к наблюдаемой коллекции. Вот мой код: (вы можете следить за этой частью, классы очень просты)
Элемент класса:
/// <summary>
/// Represent each row in listview
/// </summary>
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
void UpdateSum()
{
Sum = Col1;// + col2 + col3 etc
}
decimal _Col1;
public decimal Col1 // ||
{ // ||
get // ||
{ // ||
return _Col1; // ||
} // ||
set // ||
{ // \ || /
if (value > 100) // \ || /
{ // \/
Col1 = 100; // !!!!!!!!!!!!!!!!!!!!! HERE why does the listview does't update!!!!!!!!
NotifyPropertyChanged("Col1");
}else
{
_Col1 = value;
}
UpdateSum();
NotifyPropertyChanged("Col1");
}
}
decimal _Sum;
public decimal Sum
{
get
{
return _Sum;
}
set
{
_Sum = value;
NotifyPropertyChanged("Sum");
}
}
}
Код позади
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Item> Collection = new ObservableCollection<Item>();
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Collection.Add(new Item());
listView2.DataContext = Collection;
listView2.ItemsSource = Collection;
listView2.IsSynchronizedWithCurrentItem = true;
}
}
}
Listview в xaml:
<ListView Name="listView2" >
<ListView.View>
<GridView>
<GridViewColumn Header="Column1" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="200" Text="{Binding Col1, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Sum" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="200" Text="{Binding Sum, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
В любом случае, почему, когда я обновляю Col1=100
он не обновляется в списке! Также обратите внимание, что сумма составляет 100, а не 1000.
Я не хочу, чтобы column1 было больше некоторого числа x. В моей реальной программе это число изменяется динамически, и я вычисляю его внутри класса Item.
Как я могу это исправить?
редактировать
Я нашел кое-что интересное... Если я начну печатать разные цифры, взглянем на то, что происходит: я просто наберу 5 в этом примере:
Работает на шаге 3!!!
как только он становится равным 100, он перестает работать...
3 ответа
Я заставил это работать!!! Я изменился:
Text="{Binding Col1, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
за:
Text="{Binding Col1, Mode=TwoWay}"
Но мне нужно обновить свойство, когда текстовое поле изменяется, так что это может быть решением для кого-то еще...
В основном вы пытаетесь изменить значение свойства во время привязки данных. Проблема в том, что WPF старается быть умным и не прислушиваться к изменениям свойств, возникающих во время привязки данных. Это хорошо известная проблема с многочисленными обходными путями:
- Изменить значение в свойстве сеттера при использовании двусторонней привязки данных WPF
- Проблема привязки данных в WPF: с решением
Хотя MS сделала некоторые исправления в WPF4.0, см.: WPF 4.0 Изменение привязки данных (отличная функция)
Но я сделал небольшое быстрое тестирование, и ни один из них не работает в вашем случае из-за UpdateSourceTrigger=PropertyChanged
,
Однако я могу найти очень грязный обходной путь, который "работает":
public decimal Col1
{
get { return _Col1; }
set
{
//Change the property value based on condition
_Col1 = value > 100 ? 100 : value;
UpdateSum();
//HACK: Simulate that the property change not fired from the setter
Dispatcher.CurrentDispatcher
.BeginInvoke(new Action(() => NotifyPropertyChanged("Col1")));
//HACK: Cancel the bindig based on condition
if (value > 100)
throw new Exception();
}
}
Смотрите это действие:
Обратите внимание: после ввода третьего 5 курсор перемещает начало TextBox и остается там, если вы введете следующие 5.
Я не думаю, что вышеупомянутый код должен быть решением, я просто немного поиграл. Я думаю, что вы должны пойти с UpdateSourceTrigger=LostFocus
и сделайте привязку вручную из текстового поля TextChanged
событие... Но я боюсь, что нет чистого решения для вашей проблемы.
По сути, в TextBox есть ошибка, из-за которой, если вы измените связанное значение таким образом, обновятся только первые символы TextBox. Например, если вы ввели 1005, он обновляет первые 3 символа до 100 (но игнорирует 5).
Исправить это просто, я добавил еще одно свойство в ваш класс Item и немного изменил привязку TextBox:
public class Item : INotifyPropertyChanged
{
private int maxValue = 100;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
void UpdateSum()
{
Sum = Col1;// + col2 + col3 etc
}
decimal _Col1;
public decimal Col1
{
get
{
return _Col1;
}
set
{
if (value > maxValue)
{
Col1 = maxValue;
NotifyPropertyChanged("Col1");
}
else
{
_Col1 = value;
}
UpdateSum();
NotifyPropertyChanged("Col1");
}
}
public int MaxValueWidth
{
get
{
var tmp = (int)Math.Log10(maxValue) + 1;
return tmp;
}
}
decimal _Sum;
public decimal Sum
{
get
{
return _Sum;
}
set
{
_Sum = value;
NotifyPropertyChanged("Sum");
}
}
}
Обратите внимание, что я добавил свойство, которое вычисляет максимальные символы для базы TextBox на основе максимального значения.
Нет, все, что я делаю, это добавляю привязку
<DataTemplate>
<TextBox Width="200" Text="{Binding Col1, UpdateSourceTrigger=PropertyChanged}" MaxLength="{Binding MaxValueWidth}"></TextBox>
</DataTemplate>