ven*_*eis 16 c# windows-runtime winrt-xaml win-universal-app
我目前正在尝试新编译的绑定,并且已经达到(再次)我错过了一个难题的一个点:为什么我必须打电话Bindings.Update?到现在为止,我认为实施 INotifyPropertyChanged已经足够了?
在我的例子中,如果我调用这个神秘的方法(由编译的绑定自动生成),GUI只显示正确的值.
我正在使用具有以下(此处简化的)xaml语法的用户控件:
<UserControl>
<TextBlock Text="x:Bind TextValue"/>
</UserControl>
Run Code Online (Sandbox Code Playgroud)
where TextValue是此用户控件的简单依赖项属性.在页面中,我将此控件用作:
<Page>
<SampleControl TextValue="{x:Bind ViewModel.Instance.Name}"/>
</Page>
Run Code Online (Sandbox Code Playgroud)
哪里:
ViewModel是一个在InitializeComponent()运行之前设置的标准属性Instance 是一个简单的实现对象 INotifyPropertyChanged加载后Instance,我提出了一个属性更改事件Instance.我甚至可以调试到TextValue用户控件的depency属性获取正确值的行 - 但是没有显示任何内容.只有在我打电话时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;
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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 ????? */
}
}
}
Run Code Online (Sandbox Code Playgroud)
还有一个更新
我更新了Load使用任务的方法(参见上面的代码)!
Jer*_*xon 41
有时,您要显示的数据在页面加载和呈现后几秒钟内不可用(如从服务器或数据库返回).如果您在后台/异步进程中调用数据,从而释放UI以在不挂起的情况下呈现,则尤其如此.
到目前为止有道理吗?
现在创建一个绑定; 让我们说这样的话:
<TextBlock Text="{x:Bind ViewModel.User.FirstName}" />
Run Code Online (Sandbox Code Playgroud)
代码隐藏中的ViewModel属性的值将具有实际值,并且绑定得很好.另一方面,您的用户将没有值,因为它尚未从服务器返回.因此,既不能显示用户的FirstName属性,也不能显示用户的FirstName属性,对吧?
然后您的数据会更新.
当您将User对象的值设置为真实对象时,您会认为绑定会自动更新.特别是如果你花时间把它变成了一个INotifyPropertyChanged属性,对吧?传统的{Binding}也是如此,因为默认的绑定模式是OneWay.
什么是OneWay绑定模式?
OneWay绑定模式意味着您可以更新实现INotifyPropertyChanged的后端模型属性,并且绑定到该属性的UI元素将反映数据/值更改.太棒了.
为什么不起作用?
这不是因为{x:Bind}不支持Mode = OneWay,而是因为它默认为Mode = OneTime.回顾一下,传统的{Binding}默认为Mode = OneWay,编译后的{x:Bind}默认为Mode = OneTime.
什么是OneTime绑定模式?
OneTime绑定模式意味着在使用绑定加载/呈现UI元素时,只绑定到底层模型一次.这意味着如果您的基础数据尚不可用,则无法显示该数据,一旦数据可用,它将不会显示该数据.为什么?因为OneTime不监视INotifyPropertyChanged.它只在加载时读取.
模式(来自MSDN):对于OneWay和TwoWay绑定,对源的动态更改不会自动传播到目标,而不提供源的某些支持.您必须在源对象上实现INotifyPropertyChanged接口,以便源可以通过绑定引擎侦听的事件报告更改.对于C#或Microsoft Visual Basic,请实现System.ComponentModel.INotifyPropertyChanged.对于Visual C++组件扩展(C++/CX),实现Windows :: UI :: Xaml :: Data :: INotifyPropertyChanged.
如何解决这个问题呢?
有几种方法.第一个也是最简单的是将绑定更改="{x:Bind ViewModel.User.FirstName}为="{x:Bind ViewModel.User.FirstName, Mode=OneWay}.这样做将监视INotifyPropertyChanged事件.
现在是时候警告你默认情况下使用OneTime是{x:Bind}尝试提高绑定性能的众多方法之一.这是因为OneTime是最快的,内存需求最少.更改绑定到OneWay会破坏它,但可能需要您的应用程序.
修复此问题的另一种方法仍然是保持{x:Bind}开箱即用的性能优势,即Bindings.Update();在您的视图模型完全准备好您的数据以进行呈现后调用.如果您的工作是异步的,这很容易 - 但是,就像上面的示例一样,如果您不能确定计时器可能是您唯一可行的选择.
当然,这很糟糕,因为计时器意味着时钟时间,而在像手机这样的慢速设备上,时钟时间可能不适用.这是每个开发人员必须针对他们的应用程序进行解决的问题 - 也就是说,您的数据何时完全加载并准备就绪?
我希望这可以解释发生了什么.
祝你好运!