绑定和x:使用TwoWay模式绑定问题

时间:2016-05-13 19:10:30

标签: c# windows data-binding win-universal-app windows-10

我遇到了奇怪的问题,我无法理解。在主页面中,我只有一个按钮导航到第二页并保存我的模型:

public class Model : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void RaiseProperty(string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

    private int index = 0;
    public int Index
    {
        get { Debug.WriteLine($"Getting value {index}"); return index; }
        set { Debug.WriteLine($"Setting value {value}"); index = value; RaiseProperty(nameof(Index)); }
    }
}

public sealed partial class MainPage : Page
{
    public static Model MyModel = new Model();

    public MainPage()
    {
        this.InitializeComponent();
        SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
        SystemNavigationManager.GetForCurrentView().BackRequested += (s, e) => { if (Frame.CanGoBack) { e.Handled = true; Frame.GoBack(); } };
    }

    private void Button_Click(object sender, RoutedEventArgs e) => Frame.Navigate(typeof(BlankPage));
}

在第二页上只有 ComboBox ,它在 SelectedIndex 中有双向绑定:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ComboBox SelectedIndex="{x:Bind MyModel.Index, Mode=TwoWay}">
        <x:String>First</x:String>
        <x:String>Second</x:String>
        <x:String>Third</x:String>
    </ComboBox>
</Grid>
public sealed partial class BlankPage : Page
{
    public Model MyModel => MainPage.MyModel;

    public BlankPage()
    {
        this.InitializeComponent();
        this.Unloaded += (s, e) => Debug.WriteLine("--- page unloaded ---");
        DataContext = this;
    }
}

没什么特别的。问题是,当我使用Bindingx:Bind时,我得到两个不同的输出,但最糟糕的是,在每个新导航到同一页面之后,属性的getter(以及x:Bind中的setter)是越来越多地打电话:

enter image description here

页面仍驻留在内存中,仍然订阅了属性,这是可以理解的。如果我们从页面返回后运行GC.Collect(),我们将从头开始。

但是如果我们使用旧的绑定单向并选择更改事件:

<ComboBox SelectedIndex="{Binding MyModel.Index, Mode=OneWay}" SelectionChanged="ComboBox_SelectionChanged">

以及eventhandler:

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (e.RemovedItems.Count > 0 && e.AddedItems.FirstOrDefault() != null)
        MyModel.Index = (sender as ComboBox).Items.IndexOf(e.AddedItems.FirstOrDefault());
}

然后它将“正常”工作 - 只有一个getter和setter,无论我们之前导航到页面多少次。

所以我的主要问题是:

  • 单向 - 双向绑定的差异来自哪里?
  • 考虑到单向绑定只触发一次getter - 是双向所需/预期的描述行为?
  • 如果多个getter / setter被调用,你如何处理这个双向绑定?

您可以download from here的工作样本。

3 个答案:

答案 0 :(得分:2)

实际上,当您对tokenizer事件使用OneWay绑定时,更改选择后只会调用SectionChanged属性的 setter 。永远不会到达 getter 因此您没有看到多个“获取价值......”

但为什么 getter 没有被调用?

在这一行设置一个断点 -

Index

您会看到PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); 的值为PropertyChanged。所以null方法永远不会被触发。我怀疑这可能是Invoke中的传统绑定设置为ComboBox的错误。每当您更改选择时,绑定都会被破坏,因此OneWayPropertyChanged。如果您更改为使用null,则此问题就会消失。

如您所知,x:Bind只会在需要时收集已弃用的页面实例。因此,有时您会看到GC在多个位置被引用,无论您选择哪种绑定机制。

保证 getter setter 仅被调用一次的一种方法是将第二个Index的{​​{1}}更改为{{ 1}}。这样做可以确保页面的单个实例。

答案 1 :(得分:0)

即使您从新的BlankPage导航到新的GC.Collect(),其他页面仍然在内存中,并且仍然在静态模型中绑定为@KiranPaul评论。

现在,如果你,如你所评论的那样,改变为无静电且仍然表现相同,那是因为你犯了同样的错误。即使它不是静态的,你仍然使用MainPage中的相同变量。(我认为它不可能,因为它不是静态的)

因此,内存中没有PropertyChanged - ed的所有页面都会引发 public sealed partial class BlankPage : Page { public Model MyModel = new Model() { Index = MainPage.MyModel.Index }; public BlankPage() { this.InitializeComponent(); this.Unloaded += (s, e) => { MainPage.MyModel.Index = MyModel.Index; Debug.WriteLine("--- page unloaded ---"); }; DataContext = this; } } 事件。因为MyModel总是相同的。

尝试这应该工作。每次导航到BlankPage时,都会实例化一个新模型并传递索引。然后,当您卸载页面时,您更新MainPage.Model中的值。这样,当您离开BlankPage时,您将只看到Set和Get in Output。

GC.Collect()

enter image description here

或者当您离开BlankPage时,您可以:

  • 致电Binding
  • 卸载页面时取消绑定MyModel?

修改

使用GC.Collect()如果你真的很快就会这样做。我的猜测是GC.Collect()被称为

所以我搜索了一下,我发现了这个:

Binding vs. x:Bind, using StaticResource as a default and their differences in DataContext

答案是:

  

Windows 10的{x:Bind}标记扩展名 - 是{Binding}的替代品。 {x:Bind}缺少{Binding}的一些功能,但它比{Binding}在更短的时间和更少的内存中运行,并支持更好的调试。

所以Binding肯定会有所不同,它可能会调用composer.json或Unbind it self ??。也许看看x:Bind markup

enter image description here

答案 2 :(得分:0)

您可以尝试将See the list() method implementation添加到此绑定中以获得一些亮点。

另外,我建议交换这些行,使它们看起来像这样:

public int onStartCommand(Intent intent, int flags, int startId) {
    // TODO Auto-generated method stub

    System.out.println("inside on start command for service");

    myPrefs = this.getSharedPreferences("settings", this.MODE_PRIVATE);

    checkTime();

    return START_STICKY;
}

// to check if the time for alarm is here
private void checkTime() {
    // TODO Auto-generated method stub
    try{
    System.out.println("inside check time");

    Calendar cal;
    cal = Calendar.getInstance();

    if (08 == cal.get(cal.HOUR_OF_DAY) && 00 == cal.get(cal.MINUTE)) {
        // to call db and check for events
        nm = new MyNotificationManager(this);
        if (nm.checkEvent()) {
            nm.setNotifications();
        }
    }
    }catch(Exception e){
        System.out.println("inside exception for check time "+e.toString());
    }
}

它可能会搞乱你的绑定。当您调用initializeComponent时,它会构建xaml树但是对于绑定我猜它使用旧的DataContext,然后您立即更改DataContext,强制重新绑定每个属性。