2016-09-27 303 views
0

我在XAML中有以下代碼片段。TextBlock Text屬性在更新其源Binding屬性時未更新?

<Grid> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="01*"/> 
     <ColumnDefinition Width="03*"/> 
     <ColumnDefinition Width="01*"/> 
    </Grid.ColumnDefinitions> 

    <Button Name="btnPrevious" Grid.Column="0" Content="Previous" Click="btnPrevious_Click"/> 
    <TextBlock Name="txtBlockName" Grid.Column="1" Text="{Binding SelectedName, UpdateSourceTrigger=PropertyChanged}" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center"/> 
    <Button Name="btnNext" Grid.Column="2" Content="Next" Click="btnNext_Click"/> 

</Grid> 

這會生成如圖1所示的以下輸出。

Figure1.

這背後的代碼如下所示。

public partial class MainWindow : Window, INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    public List<string> namesList = new List<string>(); 

    public string SelectedName 
    { 
     get 
     { 
      return namesList[1]; 
     } 
     set 
     { 
      if (value != namesList[1]) 
      { 
       namesList[1] = value; 
       NotifyPropertyChanged("SelectedName"); 
      } 
     } 
    } 

    public MainWindow() 
    { 
     InitializeComponent(); 

     namesList.Add("ABC"); 
     namesList.Add("DEF"); 
     namesList.Add("GHI"); 


     this.DataContext = this; 
    } 

    private void btnPrevious_Click(object sender, RoutedEventArgs e) 
    { 
     string str = namesList[0]; 
     namesList[0] = namesList[1]; 
     namesList[1] = str; 

     this.IsEnabled = false; 
    } 

    private void btnNext_Click(object sender, RoutedEventArgs e) 
    { 
     string str = namesList[2]; 
     namesList[2] = namesList[1]; 
     namesList[1] = str; 

     this.IsEnabled = false; 
    } 



    protected void NotifyPropertyChanged(String propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

} 

TextBlock的該Text屬性綁定到SelectedName屬性。並且在更新SelectedName屬性時不會更新它。雖然我的班級實施了INotifyPropertyChanged界面並定義了其NotifyPropertyChanged行爲,但它仍然無效。

在調試時,我放了一些斷點來觀察SelectedName屬性值,我發現它按照要求更新,但TextBlockText屬性沒有更新。如圖2所示。

Figure2.

我已經看到了很多關於互聯網的問題和解決方案,但沒有解決我的問題。任何幫助,將不勝感激。

回答

1

到目前爲止給出的兩個答案,自帶甚至接近我認爲只有一個是一個合理的選擇是this answer(即在之後「如果你想'更新'SelectedName你可以'')中提出的第二個選項。

這就是說,在我看來,你會做好改變你的數據結構,以便更緊密地模擬用戶界面中實際發生的事情。如果你花時間這樣做,你的代碼將會更容易編寫和理解,並且會更簡單。

在這個特定的例子中,這意味着,因爲(它出現),你希望能夠瀏覽三個不同的文本值,你應該使用該屬性來呈現當前文本值,並使用索引變量來跟蹤選擇哪個值。例如:

public partial class MainWindow : Window, INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    // Don't make fields public. If you do want to expose the list 
    // use a public read-only property. And even there, think carefully 
    // about whether you want callers to be able to modify the list; you 
    // can return a `ReadOnlyCollection<string>` that wraps your list if 
    // you only want callers to be able to examine the contents, rather than 
    // modify it. 
    // 
    // Also, make any field that you never change "readonly". 
    private readonly List<string> namesList = new List<string>(); 

    // Here's the index that keeps track of which string is selected 
    private int _index; 

    public string SelectedName 
    { 
     get { return namesList[_index]; } 
    } 

    public MainWindow() 
    { 
     InitializeComponent(); 

     namesList.Add("ABC"); 
     namesList.Add("DEF"); 
     namesList.Add("GHI"); 

     this.DataContext = this; 
    } 

    private void btnPrevious_Click(object sender, RoutedEventArgs e) 
    { 
     if (_index > 0) 
     { 
      _index--; 
      NotifyPropertyChanged(nameof(SelectedName)); 
     } 

     // I don't know what you expect this to do. You are setting the window's 
     // "IsEnabled" property to false, which doesn't seem useful. More likely, 
     // you intend to set the "Previous" button's enabled state, but even there 
     // you really only want to set it to false if the _index value is 0. If the 
     // _index value had been 2 and was just set to 1, you still want the 
     // "Previous" button enabled. This would actually be an excellent place for 
     // you to learn how to implement an ICommand, to have its CanExecute() method 
     // return a value based on the _index value (where the "Previous" ICommand 
     // object would return true unless _index is 0, and the "Next" ICommand 
     // object would return true unless _index is namesList.Count - 1). Then you 
     // can bind the button's "Command" property to the appropriate ICommand 
     // object for the button and WPF will automatically deal with enabling 
     // or disabling the button according to the command's state 
     this.IsEnabled = false; 
    } 

    private void btnNext_Click(object sender, RoutedEventArgs e) 
    { 
     if (_index < namesList.Count - 1) 
     { 
      _index++; 
      NotifyPropertyChanged(nameof(SelectedName)); 
     } 

     // See comment above. 
     this.IsEnabled = false; 
    } 

    protected void NotifyPropertyChanged(String propertyName) 
    { 
     PropertyChanged?.DynamicInvoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

注意,與任何在任一兩個其他的答案中提出的修改,你還是留下了相關的每個文本值之間的導航錯誤。初始點擊一個按鈕可能會起作用,但在此之後,您的數據結構將會不一致,並且您不會得到您想要的結果。

本答案中的示例解決了所有這些問題以及您最初的擔憂。

+0

我對我的代碼和Dan的代碼感到困惑嗎?請澄清他們與我的區別。謝謝 –

+0

@Peter,100%與你在一起,我不知道「IsEnabled = false」意味着要做什麼,得出的結論是相同的,但我不想在代碼中引入太多差異來指出改變通知錯過...我認爲這是問題的意圖 – Dan

+0

@Waqas:你的原始代碼和Dan的帖子中的答案之間的顯着區別在於,當值改變時,Dan提升了'SelectedName'屬性的'PropertyChanged'事件,同時你的代碼沒有。只有第一次點擊按鈕時,你的兩個代碼示例才能正常工作。我的例子與你的代碼有相同的區別,它引發了'PropertyChanged'事件,不同之處在於它可以用於隨後的按鈕點擊,而不僅僅是第一次按鈕點擊。 –

0

你永遠不會設置「SelectedName」,你可以讓它只讀。

PropertyChanged從未被引發。

如果你希望綁定注意到它,你應該做的

NotifyPropertyChanged("SelectedName"); 

你已經改變了指數#_namesLIst 1後,

public partial class MainWindow : Window , INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    private readonly List<string> _namesList = new List<string>(); 

    public string SelectedName 
    { 
     get { return _namesList[1]; }    
    } 

    public MainWindow() 
    { 
     InitializeComponent(); 

     _namesList.Add("ABC"); 
     _namesList.Add("DEF"); 
     _namesList.Add("GHI"); 

     DataContext = this; 
    } 

    private void btnPrevious_Click(object sender, RoutedEventArgs e) 
    { 
     var str = _namesList[0]; 
     _namesList[0] = _namesList[1]; 
     _namesList[1] = str; 
     NotifyPropertyChanged(nameof(SelectedName)); 
     IsEnabled = false; 
    } 

    private void btnNext_Click(object sender, RoutedEventArgs e) 
    { 
     var str = _namesList[2]; 
     _namesList[2] = _namesList[1]; 
     _namesList[1] = str; 
     NotifyPropertyChanged(nameof(SelectedName)); 

     IsEnabled = false; 
    } 

    private void NotifyPropertyChanged(string propertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

} 

,如果你想「更新」 SelectedName你可以

public string SelectedName 
    { 
     get { return _namesList[1]; } 
     set 
     { 
      if (value == _namesList[1]) return; 
      _namesList[1] = value; 
      NotifyPropertyChanged("SelectedName"); 
     } 
    } 

    private void btnPrevious_Click(object sender, RoutedEventArgs e) 
    { 
     var str = _namesList[0]; 
     _namesList[0] = _namesList[1]; 
     SelectedName = str;    
     IsEnabled = false; 
    } 

    private void btnNext_Click(object sender, RoutedEventArgs e) 
    { 
     var str = _namesList[2]; 
     _namesList[2] = _namesList[1]; 
     SelectedName = str;    
     IsEnabled = false; 
    } 
+0

當我控制更多具有更多和不同文本屬性的文本塊來與它們綁定時,會做什麼。您的代碼始終在單擊「下一個」或「上一個」時更新屬性「SelectedName」。 –

+0

代碼不會更新SelectedName,它通知它已更改, 以及它綁定的內容將隨當前值更新 – Dan

+0

添加了一個更新選項 – Dan

0

SelectedName Setter屬性從未被調用過。所以,你可以通過使用BindingExpression.UpdateSource()方法,如下面更新TextBlock.Text,

private void btnPrevious_Click(object sender, RoutedEventArgs e) 
    { 
     string str = namesList[0]; 
     namesList[0] = namesList[1]; 
     namesList[1] = str; 
     BindingExpression be = txtBlockName.GetBindingExpression(TextBlock.TextProperty); 
     be.UpdateSource(); 

     this.IsEnabled = false; 
    }