Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 更改setter和notify的属性_C#_Xaml_Windows Store Apps_Uwp_Inotifypropertychanged - Fatal编程技术网

C# 更改setter和notify的属性

C# 更改setter和notify的属性,c#,xaml,windows-store-apps,uwp,inotifypropertychanged,C#,Xaml,Windows Store Apps,Uwp,Inotifypropertychanged,在属性的setter中,有时我必须将属性的值更改为当前正在设置的值以外的值。对于Windows应用商店应用程序,这并不是简单地引发PropertyChanged事件就可以做到的。我可以更改该值,但UI不会根据该更改自行更新 <TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> 我编写了以下方法,很好地解决了这个问题: protected void OnPropertyCh

在属性的setter中,有时我必须将属性的值更改为当前正在设置的值以外的值。对于Windows应用商店应用程序,这并不是简单地引发
PropertyChanged
事件就可以做到的。我可以更改该值,但UI不会根据该更改自行更新

<TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
我编写了以下方法,很好地解决了这个问题:

protected void OnPropertyChanged_Delayed( [CallerMemberName] string propertyName = null )
{
    Task.Delay( 1 ).ContinueWith(
        t => OnPropertyChanged( propertyName ),
        TaskScheduler.FromCurrentSynchronizationContext()
    );
}
我将其称为OnPropertyChanged,而不是
。但我最近在我的Windows应用商店应用程序中发现了一个间歇性错误,这是由用户导航离开页面后发生的
OnPropertyChanged\u延迟调用引起的。这促使我寻求另一种解决办法


是否有更好的方法从属性的setter中更改属性的值并将更改通知UI?我目前正在开发一个Windows应用商店应用程序,所以我想在这方面给出一个答案。但我最终会把它移植到UWP。这也是UWP的一个问题吗?如果是这样的话,我也希望有一个解决方案(可能是相同的)。

我在UWP中这样做没有问题。我不知道Windows应用商店的应用程序有什么不同

private string _informativeMessage;
public string InformativeMessage
{
    get { return _informativeMessage; }
    set
    {
        if (_informativeMessage != value)
        {
            if(value == SomeValueThatMakesMeNeedToChangeIt)
            {
                _informativeMessage = "Overridden Value";
            }
            else
            {
                _informativeMessage = value;
            }

            RaisePropertyChanged();
        }
    }
}

您的属性是什么样子的?

正如我在上面的评论中所解释的,我在处理ComboBox控件时遇到了这种问题。我想更改setter中选定项的值,但视图忽略我的新值。延迟PropertyChanged事件是一个丑陋的解决方法,可能会导致意外的副作用

但另一种解决此问题的方法是将SelectionChanged事件绑定到视图模型中的命令

此XAML像往常一样绑定到SelectedItem,但也使用System.Windows.Interactive绑定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>
正如我所说,我只观察到SelectedItem属性的这种行为。如果您在使用文本框时遇到问题,则可以尝试更改绑定的UpdateSourceTrigger,如下所示:

<TextBox Text={Binding MyText, Mode=TwoWay, UpdatedSourceTrigger=PropertyChanged} />
text = value == "third" ? "forth" : value;

我的问题是:在属性的setter中,如何更改正在设置的值并让UI识别该更改

双向XAML绑定假定设置为源的目标值也是源实际存储的值。它忽略刚刚设置的属性的
PropertyChanged
事件,因此在更新后不会检查源的实际值

文本框 最简单的解决方案是:将绑定设为单向,而是在
TextChanged
处理程序中更新源代码

<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
         TextChanged="TextBox_TextChanged"/>
当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"/>
组合框 @RogerN的回答有助于说明使用
SelectionChanged
可以克服这个问题,但他实际上没有说明如何更改setter中的值。我会在这里展示

<ComboBox ItemsSource="{Binding Items}"
          SelectedItem="{Binding Text, Mode=TwoWay}"
          SelectionChanged="ComboBox_SelectionChanged"/>
上面显示的
文本框
的第一种方法不适用于
组合框
,但第二种方法适用

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
}

因此,我假设您的视图模型触发事件,您的屏幕捕获事件,但该屏幕已被导航到其他位置?@CodingYoshi是的,视图模型在这些情况下从属性的setter内触发事件。我的意思是,您为什么需要
OnPropertyChanged\u Delayed
?这是一种代码气味。如果要通知属性已更改,则只需通知即可。为什么要延迟?@HappyNomad当你说“有时我必须将属性的值更改为当前设置的值以外的值”时,你没有解释为什么要延迟触发PropertyChanged事件。Mark给出的答案显示了如何同步进行。那么您在设置属性时有一个竞态条件?是否有其他控件试图更改属性,但您也想更改它?更改值时,应阻止该控制器的事件处理程序。我能给出的最便宜的方法是使用Boolean helper值来检查是否必须通知属性。(问题的根源是,当您不需要属性时,不应该首先设置它。)问题发生在
TextBox.Text
Selector.SelectedItem
的双向绑定上。如果可以发布相关代码,我可能能提供更多帮助。我添加了一个简单的例子来说明这个问题。因为它的价值。。。在UWP中,如果使用x:Bind而不是Binding,则这一点与预期一样有效。我不是说这是一个解决方案。。。只是指出一个不同。看起来你有一些方法可以用来实现你的目标。也看看我的答案。
void TextBox_LostFocus( object sender, RoutedEventArgs e )
{
    var tb = (TextBox)sender;
    var pm = (MyViewModel)DataContext;
    tb.GetBindingExpression( TextBox.TextProperty ).UpdateSource();
    pm.OnPropertyChanged( "Text" );
}
<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 = value == "third" ? "forth" : value;
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
}