2011-10-03 88 views
0

所以我陷入了一個問題,我能夠更新數據網格中的值,當文本框編輯數據網格的選擇單元格的值時,這會立即導致UI更新。 Pitfal是我不能更新依賴於這個單元格值的任意單元格。單元格/列綁定到的數據更改DataGrid不更新依賴單元格值。我有很多單元,所以調用DataGrid.Item.Refresh()的代價非常高。我試圖創建一個只引發InotifyProperty更改事件的方法,但這不會導致網格中的更新。我不知道如何強制更新GUI。DataGrid只更新選定的單元格

模型單元的代碼如下。並在其下面標識代碼。

public class ModelCell : INotifyPropertyChanged { 
    //This is a class that represents a cell that a user clicks on. it contains few items. And should have a reference to the SpreadSheet object 
    public String CellName { 
     get; 
     set; 
    } 
    public IEnumerable<String> dependents { 
     get; 
     private set; 
    } 
    public String Contents { 
     get { 
      return Host.GetCellContents(CellName); 
     } 
     set { 
      if (value != _contents) { 
       IEnumerable<String> tmpDep = Host.SetCellContents(CellName, value); 
       try { 
        if (Host.GetCellValue(CellName) is SS.FormulaError) { 
         //Revert the change. 
         Host.SetCellContents(CellName, _contents); 
        } else { 
         _contents = value; 
         NotifyPropertyChanged("Contents"); 
         NotifyPropertyChanged("Value"); 
         //Next is to notify dependents that we have changed. Since the names will be in the Format of A1 
         this.dependents = tmpDep; 
        } 
       } catch (Exception e) { 
        //We got an exception no change was made to the sheet anyway. 
        MessageBox.Show(e.Message); 
       } 
      } else NotifyPropertyChanged("Value"); 
     } 
    } 

    public int Row; 
    public int Col; 
    public override string ToString() { 
     return "THis is a test!"; 
    } 


    /// <summary> 
    /// Default initialize the empty cell to empty contents. this makes the construction of the objects simpler; 
    /// </summary> 
    private String _contents = ""; 

    /// <summary> 
    /// This value is contained in the SpreadSheet so it is automatically changed with the Contents. 
    /// </summary> 
    public String Value { 
     get { 
      try { 
       Object returned = Host.GetCellValue(CellName); 
       if (returned is String || returned is Double) 
        return returned.ToString(); 
       return ((SS.FormulaError)returned).Reason; 
      } catch (Exception e) { 
       MessageBox.Show("Cell " + this.CellName + " encountered an exception getting the value: " + e.Message); 
       return ""; 
      } 
     } 
     set { 
      //instead assign contents 
      NotifyPropertyChanged("Value"); 
     } 
    } 
    public SS.Spreadsheet Host { 
     set; 
     private get; 
    } 

    /// <summary> 
    /// Creates an empty cell with no value or contents with the specified name. 
    /// </summary> 
    /// <param name="Name">The Name of this cell generally in the format [A-Z][1-99]</param> 
    public ModelCell (String Name) { 
     this.CellName = Name; 
    } 
    public ModelCell() { 
     //Name will equal the base.ToString() 
     this.CellName = base.ToString(); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void NotifyPropertyChanged (String propertyName) { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (null != handler) { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

public MainWindow() { 
     InitializeComponent(); 
     ViewModel = new MainViewModel(); 
     DataGrid Sheet = null; 
     if (this.FindName("Sheet") is DataGrid) 
      Sheet = (DataGrid)this.FindName("Sheet"); 
     if (Sheet == null)//Exit 
      this.Close(); 
     IValueConverter converter = new ModelCellConverter(); 
     Binding bind; 
     for (int i = 0; i < 26; i++) { 
      DataGridColumn col = new DataGridTextColumn(); 
      col.Header = (char)('A' + i); 
      col.IsReadOnly = true; 
      col.Width = new DataGridLength(60); 
      col.CanUserSort = false; 
      //bind = new Binding(new String((char)('A' + i), 1)); 
      bind = new Binding("indexAbleArr[" + i + "].Value"); 
      //bind.Converter = new IdentityConverter(); 
      bind.Mode = BindingMode.OneWay; 
      ((DataGridTextColumn)col).Binding = bind; 
      Sheet.Columns.Add(col); 
     } 
     Sheet.ItemsSource = ViewModel.Rows; 

     //Current cell display 
     Label lab = null; 
     if (this.FindName("CurCell") is Label) { 
      lab = (Label)this.FindName("CurCell"); 
      bind = new Binding("ActiveCell.CellName"); 
      bind.Source = ViewModel; 
      bind.NotifyOnSourceUpdated = true; 
      lab.SetBinding(Label.ContentProperty, bind); 
     } //Don't bind the label if i cant find it 
     if (this.FindName("ValueBox") is Label) { 
      lab = (Label)this.FindName("ValueBox"); 
      bind = new Binding("ActiveCell"); 
      bind.Source = ViewModel; 
      bind.Converter = converter; 
      bind.NotifyOnSourceUpdated = true; 
      lab.SetBinding(Label.ContentProperty, bind); 
     } 
     TextBox content = null; 
     if (this.FindName("Content") is TextBox) { 
      content = (TextBox)this.FindName("Content"); 
      bind = new Binding("ActiveCell.Contents"); 
      bind.Source = ViewModel; 
      bind.Mode = BindingMode.TwoWay; 
      bind.NotifyOnSourceUpdated = true; 
      content.SetBinding(TextBox.TextProperty, bind); 
     } 
     ViewModel.ActiveCell = ViewModel.Rows[0].indexAbleArr[0]; 
} 

我使用XAML地指定佈局,但沒有綁定通過XAML這樣做沒有什麼值得一提的是在我的XAML代碼。行是包含用於綁定目的的模型單元陣列(indexAbleArray)的對象。它還爲綁定預先創建了26個需要的模型單元,不會拋出一百萬個空指針異常。

+0

我正在做類似的事情,但我使用的是GridView,在一個單獨的詳細框架中,對單個值的更新可以在GridView中改變多個值,並顯示出來,但我並不熟悉ViewModel,但我知道我必須將行綁定到一個ObservableCollection才能使它工作,我會連接一個通過轉換器的通道,這樣你就可以驗證這個叫做call並捕獲任何錯誤 – Paparazzi

+0

我跳過綁定到一個ObservableCollection,因爲我可以綁定直接到數組中的值,因爲他們有InotifyPropertyChanged事件。正如我所說的那樣,這種技術對更新的singel單元很有用,但不是潛在的依賴。 – Nathan

+0

但是網格綁定到集合上!你需要一個集合來轉發INotify – Paparazzi

回答

0

我認爲問題是您的DataGridTextColumns的綁定路徑中基於索引的列的部分indexAbleArr[" + i + "]。他們沒有參與財產變更通知。

由於BalamBalam建議作出這樣的陣列到ObservableCollection或者你應該養酒店在行級財產「indexAbleArr」當INT數組的任何細胞改變它Value改變的通知。

這兩個對我來說實際上是不完整的設計。 :(

+0

我試過用Row對象爲IndexbleArr [i ]等。我的困惑是爲什麼單元更新時有焦點,而不是當它沒有。它就好像數據網格只監聽選定單元格上的事件。 – Nathan