在属性的设置器中,有时我必须将属性的值更改为当前正在设置的值以外的值。对于Windows Store应用程序而言,简单地提升PropertyChanged
事件并不起作用。我可以更改该值,但UI不会根据该更改自行更新。
<TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
视图模型:
class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string text;
public string Text
{
get { return text; }
set
{
if ( text != value ) {
text = value == "0" ? "" : value;
OnPropertyChanged();
}
}
}
protected void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
我编写了以下方法,可以很好地解决这个问题:
protected void OnPropertyChanged_Delayed( [CallerMemberName] string propertyName = null )
{
Task.Delay( 1 ).ContinueWith(
t => OnPropertyChanged( propertyName ),
TaskScheduler.FromCurrentSynchronizationContext()
);
}
我称之为OnPropertyChanged
而不是OnPropertyChanged_Delayed
。但是我最近发现我的Windows应用商店应用中出现间歇性错误,这是由用户导航离开页面后发生processhtml: {
dist:{
options: {
process: true,
templateSettings: {
evaluate: /{\{(.+?)\}\}/g,
interpolate: /{\{=(.+?)\}\}/g,
escape: /{\{-(.+?)\}\}/g
}
},
files: [
{
expand: true,
cwd: 'dist/',
src: ['*.html'],
dest: 'dist/',
ext: '.html'
},
],
}
},
调用引起的。这导致我寻求替代解决方案。
是否有更好的方法从其设置器中更改属性的值并通知UI更改?我目前正在开发一个Windows应用商店应用,所以我想在这种情况下给出答案。但我最终会把它移植到UWP。这也是UWP的一个问题吗?如果是这样,我也想在那里找到解决方案(可能是相同的)。
答案 0 :(得分:1)
正如我在上面的评论中所解释的那样,在处理ComboBox控件时遇到了这种问题。我想更改setter中所选项的值,但视图忽略了我的新值。延迟PropertyChanged事件是一个丑陋的解决方法,可能会导致意外的副作用。
但解决此问题的另一种方法是将SelectionChanged事件绑定到视图模型中的命令。
此XAML像往常一样绑定到SelectedItem,但也使用System.Windows.Interactivity绑定SelectionChanged事件:
<ComboBox ItemsSource="{Binding MyItems}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
在视图模型中,不要尝试覆盖setter中的选定值。相反,在ICommand实现中更改它:
string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
}
}
// RelayCommand is just a custom ICommand implementation which calls the specified delegate when the command is executed
public RelayCommand SelectionChangedCommand
{
get
{
return new RelayCommand(unused =>
{
if (_selectedItem == MyItems[2])
_selectedItem = MyItems[0];
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
});
}
}
正如我所说,我只是观察了SelectedItem属性的这种行为。如果您遇到TextBox问题,那么您可以尝试更改绑定的UpdateSourceTrigger:
<TextBox Text={Binding MyText, Mode=TwoWay, UpdatedSourceTrigger=PropertyChanged} />
答案 1 :(得分:0)
我在UWP中这样做没有问题。我不知道Windows Store应用程序有什么不同。
{{1}}
您的财产是什么样的?
答案 2 :(得分:0)
我的问题是:从属性的setter中,如何更改正在设置的值并让UI识别出该更改?
双向XAML绑定假定设置为源的目标值也是源实际存储的值。它忽略了它刚刚设置的属性的PropertyChanged
事件,因此在更新后它不会检查源的实际值。
最简单的解决方案是:单向绑定,而是在TextChanged
处理程序中更新源。
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
TextChanged="TextBox_TextChanged"/>
事件处理程序:
void TextBox_TextChanged( object sender, TextChangedEventArgs e )
{
var tb = (TextBox)sender;
var pm = (MyViewModel)DataContext;
pm.Text = tb.Text; //update one-way binding's source
}
当UI的文本发生变化时,通过设置视图模型的文本来处理事件。视图文本的单向绑定意味着它接收实际设置的值。这样做可以避免上面提到的XAML绑定的限制。
可以将绑定保留为双向,上述解决方案仍然有效。但是如果你想让双向绑定更新它的源代码,那么代码会稍微长一些:
void TextBox_TextChanged( object sender, TextChangedEventArgs e )
{
var tb = (TextBox)sender;
var pm = (MyViewModel)DataContext;
tb.GetBindingExpression( TextBox.TextProperty ).UpdateSource();
if ( tb.Text != pm.Text )
tb.Text = pm.Text; //correct two-way binding's target
}
如果您不需要UpdateSourceTrigger=PropertyChanged
,则另一种选择是:
<TextBox Text="{Binding Text, Mode=TwoWay}" LostFocus="TextBox_LostFocus"/>
使用事件处理程序:
void TextBox_LostFocus( object sender, RoutedEventArgs e )
{
var tb = (TextBox)sender;
var pm = (MyViewModel)DataContext;
tb.GetBindingExpression( TextBox.TextProperty ).UpdateSource();
pm.OnPropertyChanged( "Text" );
}
@ RogerN的回答有助于显示使用SelectionChanged
可以解决问题,但他实际上并没有显示如何更改setter中的值。我会在这里展示一下。
<ComboBox ItemsSource="{Binding Items}"
SelectedItem="{Binding Text, Mode=TwoWay}"
SelectionChanged="ComboBox_SelectionChanged"/>
将此属性添加到视图模型:
public IList<string> Items
{
get { return items; }
}
readonly IList<string> items = new ObservableCollection<string> { "first", "second", "third", "forth" };
在Text
属性的setter中,更改值如下:
text = value == "third" ? "forth" : value;
上面显示的TextBox
的第一种方法不适用于ComboBox
,但第二种方法适用。{/ p>
void ComboBox_SelectionChanged( object sender, SelectionChangedEventArgs e )
{
var cb = (ComboBox)sender;
var pm = (MyViewModel)DataContext;
cb.GetBindingExpression( ComboBox.SelectedItemProperty ).UpdateSource();
if ( (string)cb.SelectedItem != pm.Text )
cb.SelectedItem = pm.Text; //correct two-way binding's target
}