2016-05-13 58 views
2

我遇到了奇怪的問題,我不明白。在主網頁我只有一個按鈕,導航到第二頁,並擁有我的模型:綁定和x:綁定雙向模式的問題

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)); 
} 

在第二頁,只有組合框有兩個方式的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(和setter在x:Bind)後調用更多次:

enter image description here

頁仍駐留在內存中,仍然訂閱屬性,這是可以理解的。如果我們從頁面返回後運行GC.Collect(),我們將從頭開始。

但是,如果我們使用舊綁定單向和選擇更改事件:

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

隨着事件處理程序:

​​

那麼它會工作 '正常' - 無論我們之前導航到頁面的次數多少,只有一個getter和setter。

所以我的主要問題是:

  • 其中單向這種差異 - 雙向結合從何而來?
  • 考慮到單向綁定大火只能吸氣一次 - 描述的行爲是雙向想要/打算嗎?
  • 如何處理這個雙向綁定多個getter/setters被調用的情況下?

一個工作樣品,你可以download from here

+1

這是因爲你的靜態模型實例嗎?只是我的猜測。 –

+0

@KiranPaul不,非靜態的行爲完全一樣。我幾乎可以肯定它與內存有某種聯繫 - 如果我在返回主頁後觸發'GC.Collect()',那麼我又回到了開始。儘管如此,我不知道爲什麼這兩個綁定差異如此之大,爲什麼單向getter總是被調用一次。 – Romasz

回答

2

其實當你使用OneWaySectionChanged事件綁定,Index財產只有二傳手正在改變選擇後調用。 getter永遠不會到達,因此您看不到多個「正在獲取值...」

但是爲什麼getter沒有叫?

穿上這條線斷點 -

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); 

您將看到的PropertyChangednull。所以Invoke方法永遠不會被解僱。我懷疑這可能是ComboBox中的一個bug,傳統綁定設置爲OneWay。無論何時更改選擇,綁定都會中斷,因此PropertyChangednull。如果您更改爲使用x:Bind,則此問題消失。

如您所知,GC只會在需要時收集已放棄的頁面實例。所以有時候你看到Index被多個地方引用,不管你選擇了哪種綁定機制。

的一種方式,以保證吸氣二傳手只得到調用一次是改變你的第二個PageEnabled/Required的的NavigationCacheMode。這樣做將確保頁面的單個實例。

+1

我完全忘了* NavigationCacheMode *也很好的捕捉這個屬性改變了beng null。謝謝你的幫助。 – Romasz

+0

你是歡迎,你總是有有趣的問題:) –

0

即使在你從一個新的BlankPage導航到新的BlankPage之後,其他頁面仍然在內存中,並且仍然作爲@KiranPaul評論的靜態模型綁定。

現在,如果你像你所說的那樣,改變爲無靜態並且仍然表現相同,那是因爲你犯了同樣的錯誤。即使它不是靜態的,你仍然使用MainPage中的相同變量(我認爲它不可能,但它不是靜態的)

因此,所有內存中沒有GC.Collect() -ed的頁面將獲得PropertyChanged事件引發。因爲MyModel總是一樣的。

試試這個應該可以。每次您導航到BlankPage時,您都會實例化一個新模型並傳遞您的索引。然後,當您卸載該頁面時,您將更新MainPage.Model中的值。這樣,當你離開BlankPage時,你只會看到一個Set和一個Get in Output。

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; 
     } 
    } 

enter image description here

或者當你離開BlankPage您可以:

  • 呼叫GC.Collect()
  • 取消綁定爲MyModel當你卸載的頁面?

編輯:

隨着Binding它也做了同樣的,如果你這樣做真快。我的猜測是,GC.Collect()被稱爲

所以我搜索了一下,我發現這一點:

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

的回答說:

的{X:}綁定標記擴展 - 新的Windows 10是{Binding}的替代方案。 {x:Bind}缺少{Binding}的一些功能,但它比{Binding}更少的時間和更少的內存運行,並且支持更好的調試。

因此,綁定肯定會有所不同,它可能會調用GC.Collect()或解除綁定自我??。也許有x:Bind markup

enter image description here

+0

幾個問題然後:1)如果我想使用我的應用程序中定義的一個模型,而不是重新創建它? 2)爲什麼'Binding'和'x:Bind'表現不一樣? 3)爲什麼單向綁定能正常工作呢? - 總是一個getter,不管我多少次導航到一個頁面(使用靜態和非靜態的?) – Romasz

+0

@Romasz檢查我的編輯,如果解除綁定或調用'GC.Collect()'我認爲你沒問題 – Stamos

+0

肯定綁定不調用'GC.Collect()',你甚至可以在調試的時候檢查它,並且我不確定你的意思是什麼*它可以解除綁定* – Romasz

0

看看你可以嘗試添加tracing這種結合一些啓發。

此外,我會建議讓他們看起來像這樣來交換這些行:

DataContext = this; 
this.InitializeComponent(); 

這可能與你結合搞亂。當你調用initializeComponent時,它會構建xaml樹,但對於綁定,我想它會使用舊的DataContext,然後立即更改DataContext,從而強制重新綁定每個屬性。