我目前正在尝试使用新的已编译绑定(bind),并且(再次)达到了我在拼图中错过一分钱的地步:为什么我必须调用Bindings.Update?直到现在,我还认为实现INotifyPropertyChanged足够了?

在我的示例中,如果我调用此神秘方法(由编译的绑定(bind)自动生成),则GUI仅显示正确的值。

我正在使用具有以下xaml语法的用户控件:

<UserControl>
  <TextBlock Text="x:Bind TextValue"/>
</UserControl>

其中TextValue是此用户控件的简单依赖项属性。在页面中,我将此控件用作:
<Page>
  <SampleControl TextValue="{x:Bind ViewModel.Instance.Name}"/>
</Page>

在哪里:
  • ViewModel是在运行InitializeComponent()之前设置的标准属性
  • Instance是实现INotifyPropertyChanged的简单对象

  • 加载Instance后,我引发了Instance的属性更改事件。我什至可以调试到该行,在该行中,用户控件的依赖属性TextValue获得正确的值,但是什么也不显示。仅当我调用Bindings.Update()时,才会显示该值。我在这里想念什么?

    更新

    我也不使用{x:Bind ... Mode=OneWay}

    更多代码

    Person.cs:
    using System.ComponentModel;
    using System.Threading.Tasks;
    
    namespace App1 {
        public class Person : INotifyPropertyChanged {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private string name;
            public string Name { get {
                    return this.name;
                }
                set {
                    name = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Name"));
                }
            }
        }
    
        public class ViewModel : INotifyPropertyChanged {
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            private Person instance;
            public Person Instance {
                get {
                    return instance;
                }
                set {
                    instance = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Instance"));
                }
            }
    
            public Task Load() {
                return Task.Delay(1000).ContinueWith((t) => {
                    var person = new Person() { Name = "Sample Person" };
                    this.Instance = person;
                });
            }
    
    
        }
    }
    

    SampleControl.cs:
    <UserControl
        x:Class="App1.SampleControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="100"
        d:DesignWidth="100">
    
        <TextBlock Text="{x:Bind TextValue, Mode=OneWay}"/>
    
    </UserControl>
    

    SampleControl.xaml.cs:
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace App1 {
        public sealed partial class SampleControl : UserControl {
    
            public SampleControl() {
                this.InitializeComponent();
            }
    
            public string TextValue {
                get { return (string)GetValue(TextValueProperty); }
                set { SetValue(TextValueProperty, value); }
            }
    
            public static readonly DependencyProperty TextValueProperty =
                DependencyProperty.Register("TextValue", typeof(string), typeof(SampleControl), new PropertyMetadata(string.Empty));
    
        }
    }
    

    MainPage.xaml:
    <Page
        x:Class="App1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <local:SampleControl TextValue="{x:Bind ViewModel.Instance.Name, Mode=OneWay}"/>
        </StackPanel>
    </Page>
    

    MainPage.xaml.cs:
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    namespace App1 {
    
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.DataContext = new ViewModel();
                this.Loaded += MainPage_Loaded;
                this.InitializeComponent();
            }
    
            public ViewModel ViewModel {
                get {
                    return DataContext as ViewModel;
                }
            }
    
            private void MainPage_Loaded(object sender, RoutedEventArgs e) {
                ViewModel.Load();
                Bindings.Update(); /* <<<<< Why ????? */
            }
        }
    }
    

    又一个更新

    我更新了Load方法以使用任务(请参见上面的代码)!

    最佳答案

    有时,要显示的数据直到页面加载并呈现几秒钟后才可用(例如从服务器或数据库返回)。如果您在后台/异步过程中调用数据以释放UI而不挂起,则尤为如此。

    到目前为止有意义吗?

    现在创建一个绑定(bind);我们这样说:

    <TextBlock Text="{x:Bind ViewModel.User.FirstName}" />
    

    后面代码中ViewModel属性的值将具有实际值,并且可以很好地绑定(bind)。另一方面,您的用户将没有值,因为它尚未从服务器返回。结果既不能显示用户的名字,也不能显示用户的名字,对吗?

    然后更新您的数据。

    您认为将User对象的值设置为真实对象时,绑定(bind)会自动更新。特别是如果您花时间使其成为INotifyPropertyChanged属性,对吗?对于传统的{Binding}来说,这是正确的,因为默认的绑定(bind)模式是OneWay。

    什么是OneWay绑定(bind)模式?

    OneWay绑定(bind)模式意味着您可以更新实现INotifyPropertyChanged的后端模型属性,并且绑定(bind)到该属性的UI元素将反射(reflect)数据/值更改。太好了

    为什么不起作用?

    这不是因为{x:Bind}不支持Mode = OneWay,而是因为它默认为Mode = OneTime。回顾一下,传统的{Binding}默认为Mode = OneWay,编译的{x:Bind}默认为Mode = OneTime。

    什么是OneTime绑定(bind)模式?

    OneTime绑定(bind)模式意味着在使用绑定(bind)加载/渲染UI元素时,仅绑定(bind)一次到基础模型。这意味着,如果您的基础数据尚不可用,它将无法显示该数据,并且一旦数据可用,将不会显示该数据。为什么?因为OneTime不监视INotifyPropertyChanged。它仅在加载时读取。



    如何解决此问题?

    有几种方法。首先也是最简单的方法是将绑定(bind)从="{x:Bind ViewModel.User.FirstName}更改为="{x:Bind ViewModel.User.FirstName, Mode=OneWay}。这样做将监视INotifyPropertyChanged事件。



    解决此问题并保持{x:Bind}开箱即用的性能优势的另一种方法是,在 View 模型完全准备好要呈现的数据之后,调用Bindings.Update();。如果您的工作是异步的,那么这很容易-但是,就像上面的示例一样,如果您不确定计时器可能是唯一可行的选择,那么这很容易。



    我希望这可以解释正在发生的事情。

    祝你好运!

    关于c# - 使用已编译的绑定(bind)(x :bind),,为什么我必须调用Bindings.Update()?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33070705/

    10-17 02:40