【问题标题】:DataGrid only updating selected cellDataGrid 仅更新选定的单元格
【发布时间】:2011-10-03 18:58:52
【问题描述】:

所以我遇到了一个问题,当文本框编辑选择数据网格的单元格的值时,我能够更新数据网格中的值,这会导致 UI 立即更新。陷阱是我无法更新依赖于该单元格值的任意单元格。单元格/列绑定到的数据会发生变化,但 DataGrid 不会更新依赖的单元格值。我有很多单元格,因此调用 DataGrid.Item.Refresh() 的成本非常高。我尝试创建一个只引发 InotifyProperty 更改事件的方法,但这不会导致网格更新。我不知道如何强制 GUI 更新。

模型单元的代码如下。并且投标代码在它下面。

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 代码中没有什么值得注意的。 Rows 是一个包含用于绑定目的的模型单元数组 (indexAbleArray) 的对象。它还为绑定预先创建了 26 个所需的模型单元,以免引发一百万个空指针异常。

【问题讨论】:

  • 我正在做类似的事情。但我正在使用 GridView。在单独的详细信息框架中,对单个值的更新可以更改 GridView 中的多个值并且确实会显示出来。我对 ViewModel 并不熟悉,但我知道我必须将行绑定到 ObservableCollection 才能使其工作。我会连接一个直通转换器,这样您就可以验证它是否调用调用并捕获任何错误。
  • 我跳过绑定到 ObservableCollection,因为我可以直接绑定到数组中的值,因为它们有 InotifyPropertyChanged 事件。正如我之前提到的,这种技术非常适用于正在更新的单个细胞,但不适用于潜在的依赖。
  • 但是 Grid 是绑定到集合的!您需要一个转发 INotify 的集合。

标签: c# wpf xaml binding wpfdatagrid


【解决方案1】:

我认为问题在于您在 DataGridTextColumns 的绑定路径中基于索引的列 indexAbleArr[" + i + "] 的一部分。他们不参与属性更改通知。

正如 BalamBalam 建议的那样,将该数组设置为 ObservableCollection,或者当该数组中的任何单元格更改它 Value 时,您应该在属性“indexAbleArr”的行级别引发属性更改通知。

对我来说,两者实际上都是不完整的设计。 :(

【讨论】:

  • 我已经尝试使用 Row 对象为 IndexbleArr[i] 等的所有变体发起事件。我的困惑是为什么单元格在有焦点时会更新,而不是在没有焦点时更新。就好像数据网格只监听所选单元格上的事件一样。
猜你喜欢
  • 2014-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-14
  • 2015-08-24
  • 2022-01-15
相关资源
最近更新 更多