2016-09-23 22 views
0

即使在審查了大量解決方案建議後,我仍無法在xaml中獲得簡單的雙向綁定工作。我有一個窗口,一個數據上下文和一個應用程序。問題是:WPF雙向綁定無法在CheckBox和列表上工作<string>

a)雖然App構造函數運行時,窗口(在同一構造函數中初始化和.Show -ed)顯示出來,但根本沒有更新,即使我切換了C#代碼中的複選框值幾次; b)當App構造函數完成時,窗口只更新一次;我已經設置好了,如果我點擊窗口中的複選框,App中的事件處理程序(綁定到DataContext屬性更改通知)應增加字符串列表的大小,這也會顯示出來。代碼中增加的列表正確發生,但不反映在窗口中。

摘要:在Window

  • 用戶輸入到應用程序的C#代碼罰款:我可以在複選框的變化等行動
  • 相反的方向不起作用:每當項目在改變通過代碼的dataContext,即使iNotifyProperty被實現並執行,Window也不會自動更新。

我希望那是什麼

一),而應用程序的構造和運行的切換複選框值,窗口應該反映通過設置/清除蜱包裝盒上的變化;

b)在App構造函數完成後,只要將CheckBox從FALSE切換到TRUE,NameList就會附加一個新字符串。我希望窗口中的列表可以相應增加,並自動顯示完整的附加NameList內容。

觀察

  • 我儘量確保在窗口的DataContext是在窗口調用InitializeComponent之前設置。沒有真正不幸的是區別...
  • 我得到的MainWindow.xaml文件在VS一個線索:複選框路徑以及列表框綁定NameListCannot resolve symbol due to unknown DataContext 然而,當應用程序註釋構造函數終止窗口被更新,當我點擊CheckBox時,觸發正確的NotifyProperty事件。這告訴我,運行時綁定實際上應該工作...顯然只有單向,但不是雙向。

MainWindow.xaml:

<Window x:Class="StatisticsEvaluation.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
<Grid> 
    <StackPanel Orientation="Vertical"> 

     <CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="CheckBox" /> 

     <ListBox ItemsSource="{Binding NameList, Mode=TwoWay}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel> 
         <TextBlock Text="{Binding}"/> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 

     <TextBlock FontSize="18" FontFamily="Arial" Foreground="Black" Text="TextBlock" Visibility="Visible" /> 

    </StackPanel> 
</Grid> 

MainWindow.xaml。CS:

namespace StatisticsEvaluation 
{ 
/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    public MainWindow() 
    {    
    } 
} 

}

的App和的DataContext:

namespace StatisticsEvaluation 
{ 
    public class DataContextClass : INotifyPropertyChanged 
    { 
     private bool isChecked; 

     public bool IsChecked 
     { 
      get 
      { 
       return isChecked; 
      } 

      set 
      { 
       isChecked = value; 
       OnPropertyChanged("IsChecked"); 
      } 
     } 

     private List<string> nameList; 

     public List<string> NameList 
     { 
      get 
      { 
       return nameList; 
      } 

      set 
      { 
       nameList = value; 
       OnPropertyChanged("NameList"); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     public void OnPropertyChanged(string propertyName) 
     { 
      var handler = PropertyChanged; 
      if (handler != null) 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 


    /// <summary> 
    /// Interaction logic for App.xaml 
    /// </summary> 

    public partial class App : Application 
    { 
     private MainWindow MyWindow { get; set; } 

     private DataContextClass MyDataContext{ get; set; } 

     private void HandleDataContextPropertyChange(object sender, PropertyChangedEventArgs e) 
     { 
      // If the CheckBox was just toggled to TRUE, increase the NameList 
      // with an additional name and call OnPropertyChanged on it ... 
      // hoping that this would trigger a Window UI update - but no luck ! 

      if ((e.PropertyName == "IsChecked") && MyDataContext.IsChecked) 
      { 
       var randomProvider = new Random(); 
       MyDataContext.NameList.Add(randomProvider.Next().ToString()); 
       MyDataContext.OnPropertyChanged("NameList"); 
      } 
     } 

     public App() 
     { 
      MyDataContext = new DataContextClass(); 
      MyDataContext.PropertyChanged += HandleDataContextPropertyChange; 

      MyWindow = new MainWindow {DataContext = MyDataContext}; 
      MyWindow.InitializeComponent(); 
      MyWindow.Show(); 

      MyDataContext.NameList = new List<string>(); 
      MyDataContext.NameList.Add("FirstName"); 
      MyDataContext.NameList.Add("SecondName"); 
      MyDataContext.NameList.Add("ThirdName"); 

      MyDataContext.IsChecked = true; 
      Thread.Sleep(3000); 
      MyDataContext.IsChecked = false; 
      Thread.Sleep(3000); 
      MyDataContext.IsChecked = true; 
     }  
    } 
} 

當我啓動App出現如下窗口,一旦應用程序構造命中.Show

enter image description here

一旦應用程序構造器h作爲成品,窗口更新一次,但從來沒有一次後,不管有多少字符串被添加到NameList

enter image description here

任何想法,爲什麼我的雙向綁定只在一個方向的工作?

+1

我建議你刪除所有奇怪的'MyDataContext'東西在'App'和剛剛創建的MainWindow的構造函數什麼的視圖模型。此外,對於將綁定到WPF中的控件的集合,始終使用'ObservableCollection '。不要使用列表。它的內容改變時不會引發事件。 –

+2

'ItemsSource =「{Binding NameList,Mode = TwoWay}」'沒有意義。一個ItemsControl從不改變它的'ItemsSource'屬性,所以設置'Mode = TwoWay'永遠不會有任何效果。除此之外,'MyDataContext.OnPropertyChanged(「NameList」);'因爲NameList實例沒有改變而被忽略。所以使用ObservableCollection。 – Clemens

+0

先生們 - 謝謝!事實上,我的主要錯誤是不使用ObservableCollection。我還刪除了NameList綁定中的'Mode = TwoWay',正如你所提到的那樣,Clemens - 它確實沒有任何作用(並且沒有顯式設置TwoWay模式而完美工作)。感謝您指出了這一點。 Ed,我將在MainWindow中嘗試創建視圖模型。我現在的代碼更多的是對自己的可行性檢查。 (和我有限的WPF知識)導致了不尋常的結構。我會糾正這一點。 – Woelund

回答

0

如果綁定集合未實現INotifyCollectionChanged(例如ObservableCollection<T>),則在嘗試更新視圖時會出現不一致或不存在的行爲。我注意到,在將檢查狀態切換爲真後,當我們滑動鼠標的滾輪時,列表確實會更新。另外,正如@Clemens所說,你的ItemsSource綁定應該是Mode=TwoWay,因爲這是唯一合理的模式。

順便說一句,無論如何,你應該使用符合INotifyCollectionChanged的集合,因爲如果在完成時沒有清除綁定,則可能會遇到泄漏[1]。這在您的單窗口應用程序中不是問題,但現在值得一提。

至於睡覺之間的來回切換IsChecked,我的猜測是,Thread.Sleep是在UI線程上發生的(因而綁起來),因此你的死區時間,其中PropertyChanged是無用的6秒。我可以用下面來解決這個(假設正確的集合類型正在使用):

private async void Toggle() 
{ 
    MyDataContext.IsChecked = true; 
    await Task.Delay(3000); 
    MyDataContext.IsChecked = false; 
    await Task.Delay(3000); 
    MyDataContext.IsChecked = true; 
} 

,並在App構造年底Toggle()通話。這不幸的是,應用程序試圖從不工作的不同線程修改集合。然後,您可以解決有喜歡的事,可笑:

 ... 
     Toggle(Application.Current.Dispatcher); 
    } 

    private async void Toggle(System.Windows.Threading.Dispatcher d) 
    { 
     d.Invoke(() => { MyDataContext.IsChecked = true; }); 
     await Task.Delay(3000); 
     d.Invoke(() => { MyDataContext.IsChecked = false; }); 
     await Task.Delay(3000); 
     d.Invoke(() => { MyDataContext.IsChecked = true; }); 
    } 

但是這只是強制執行程序的貧困人口結構。 編輯:我忘了提及使用異步/等待有釋放UI線程的額外好處;它不再在檢查狀態之間鎖定整個窗口。

我建議你將你的代碼分離到適當的文件中,然後將邏輯分離到適當的位置。您的HandleDataContextPropertyChange可能發生在設置者IsChecked的減去通知呼叫。

[1] https://blog.jetbrains.com/dotnet/2014/09/04/fighting-common-wpf-memory-leaks-with-dotmemory/

+0

感謝布蘭登 - 像魅力一樣工作!非常感謝你的努力。我現在也看到,移動鼠標滾輪確實更新了列表...有趣的。當然,我的主要錯誤是不使用ObservableCollection。我也曾嘗試在App構造函數的最後創建一個新線程來切換IsChecked標誌,但遇到了您提到的同一問題(集合不能從不同線程修改)。通過調度員切換(我每天都在學習:-))很好地工作。 我的代碼是可行性檢查 - 將按照您的建議改進結構。 – Woelund