简体   繁体   English

如何防止 ObservableCollection 在 WPF 中无故刷新?

[英]How can I prevent the ObservableCollection from being refreshed for no reason in WPF?

I have C# WPF project that is save data from DataGrid into a Table in SQL Server Database我有C# WPF项目,它将DataGrid中的数据保存到 SQL 服务器数据库中的表中

In Database I have two tables as Factor and Commodity that related to each other and在数据库中,我有两个表作为FactorCommodity ,它们相互关联,并且在此处输入图像描述

The DataGrid ItemsSource filled by an ObservableCollection is named WHOLE_DATA_FACTOR that is from Factor TableObservableCollection填充的 DataGrid ItemsSource名为WHOLE_DATA_FACTOR ,它来自Factor

and ItemsSource of DataGridComboBoxColumn it is filled only once when loading the program, After I Insert into Factor table in RowEditEnding event I want to Reload data for DataGrid,DataGridComboBoxColumnItemsSource在加载程序时仅填充一次,在RowEditEnding事件中插入Factor表后,我想为 DataGrid 重新加载数据,

The DataGridComboBoxColumn items were filled once by a query in the SELECT CommodityCode, CommodityName FROM dbo.Commodity , but I noticed that after LoadDataAgian , the items that were in the collection started to refresh (as if moving on each item), and this made the program slow down, and also The DataGridComboBoxColumn ItemsSource starts to fill again, while I didn't do that DataGridComboBoxColumn项目由SELECT CommodityCode, CommodityName FROM dbo.Commodity中的查询填充一次,但我注意到在LoadDataAgian之后,集合中的项目开始刷新(好像在每个项目上移动),这使得程序变慢,并且DataGridComboBoxColumn ItemsSource再次开始填充,而我没有这样做

Here is the Video what happened :这是发生了什么的视频

mp4: https://ufile.io/p5azjp40 mp4: https://ufile.io/p5azjp40

Full Source Code and DB : https://ufile.io/uxneng9r完整源代码和数据库https://ufile.io/uxneng9r


XAML : XAML

        <DataGridComboBoxColumn Width="160" Header="DataGridComboBoxColumn"
                                SelectedValueBinding="{Binding CommodityID}" 
                                DisplayMemberPath="CommodityName" 
                                SelectedValuePath="CommodityCode"
                                >

            <DataGridComboBoxColumn.ElementStyle>
                <Style TargetType="{x:Type ComboBox}">
                    <Setter Property="ItemsSource" Value="{Binding Path=TheCommodityCombo_DATA, RelativeSource={RelativeSource AncestorType=Window}}" />


                </Style>
            </DataGridComboBoxColumn.ElementStyle>

            <DataGridComboBoxColumn.EditingElementStyle>
                <Style TargetType="{x:Type ComboBox}">
                    <Setter Property="ItemsSource" Value="{Binding Path=TheCommodityCombo_DATA, RelativeSource={RelativeSource AncestorType=Window}}" />

                    <Setter Property="IsEditable" Value="True"/>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
                    <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
                    <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
                </Style>
            </DataGridComboBoxColumn.EditingElementStyle>
        </DataGridComboBoxColumn>

        <DataGridComboBoxColumn Width="200" Header="Status ComboBoxColumn"
                                SelectedValueBinding="{Binding STATUS}" 
                                DisplayMemberPath="STATUS_NAME" 
                                SelectedValuePath="STATUS">

            <DataGridComboBoxColumn.ElementStyle>
                <Style TargetType="{x:Type ComboBox}">
                    <Setter Property="ItemsSource" Value="{Binding Path=DataContext.STATUS_COMBO_DATA, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />


                </Style>
            </DataGridComboBoxColumn.ElementStyle>

            <DataGridComboBoxColumn.EditingElementStyle>
                <Style TargetType="{x:Type ComboBox}">
                    <Setter Property="ItemsSource" Value="{Binding Path=DataContext.STATUS_COMBO_DATA, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

                    <Setter Property="IsEditable" Value="True"/>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
                    <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
                    <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
                </Style>
            </DataGridComboBoxColumn.EditingElementStyle>
        </DataGridComboBoxColumn>


    </DataGrid.Columns>

</DataGrid>

Code Behind :代码背后

   public partial class MainWindow : Window
    {
        /// <summary>
        /// For Ensuring the new data is getting in Row end edit
        /// </summary>
        private bool _handle = true;
        MyerEntities dbms = new MyerEntities();


        #region Models and Collections
        /// <summary>
        /// For Factor Table
        /// </summary>
        public class MyCustomModel_Factor
        {
            public long? NUMBER { get; set; }
            public string CustomerName { get; set; }
            public long? CommodityID { get; set; }
            public long? STATUS { get; set; }
        }
        public ObservableCollection<MyCustomModel_Factor> WHOLE_DATA_FACTOR { get; set; } = new ObservableCollection<MyCustomModel_Factor>();

        /// <summary>
        /// For Commodity Table for ComboBox Items
        /// </summary>
        public ObservableCollection<MyCustomModel_Commodity> TheCommodityCombo_DATA { get; set; } = new ObservableCollection<MyCustomModel_Commodity>();
        public class MyCustomModel_Commodity
        {
            public long CommodityCode { get; set; }
            public string CommodityName { get; set; }
        }
        //STATUS
        public ObservableCollection<CutsomStatus_Model> STATUS_COMBO_DATA { get; set; } = new ObservableCollection<CutsomStatus_Model>();
        public class CutsomStatus_Model
        {
            public long? STATUS { get; set; }
            public string STATUS_NAME { get; set; }
        }
        #endregion


        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Filling DataGrid by ObservableCollection
            WHOLE_DATA_FACTOR.Clear();
            var RST = dbms.Database.SqlQuery<MyCustomModel_Factor>("SELECT * FROM Factor").ToList();
            foreach (var item in RST)
            { WHOLE_DATA_FACTOR.Add(item); }

            //Filling ComboBox from Another Table that Related to Factor Table
            TheCommodityCombo_DATA.Clear();
            var RST2 = dbms.Database.SqlQuery<MyCustomModel_Commodity>("SELECT CommodityCode, CommodityName FROM dbo.Commodity").ToList();
            foreach (var item2 in RST2)
            {
                TheCommodityCombo_DATA.Add(item2);
            }

            //STATUS Filling
            STATUS_COMBO_DATA.Add(new CutsomStatus_Model { STATUS = 1, STATUS_NAME = "Undone" });
            STATUS_COMBO_DATA.Add(new CutsomStatus_Model { STATUS = 2, STATUS_NAME = "Done" });
            STATUS_COMBO_DATA.Add(new CutsomStatus_Model { STATUS = 3, STATUS_NAME = "Canceled" });
        }

        private void MainDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            try
            {
                var WhatWasRow = e.Row.Item as MyCustomModel_Factor;
                var TheCurrentColumnName = MainDataGrid.CurrentColumn.SortMemberPath;
                var test = WhatWasRow.GetType().GetProperty(TheCurrentColumnName).GetValue(WhatWasRow);

                var Editedrow = (e.EditingElement as ComboBox);

                var test3 = ((System.Windows.Controls.Primitives.Selector)e.EditingElement).SelectedValue;
            }
            catch (Exception)
            { goto OnErrorResumeNext; }

        OnErrorResumeNext:;
        }
        private void MainDataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
        {
            if (_handle)
            {
                _handle = false;
                MainDataGrid.CommitEdit();
                var ROW_ITM = e.Row.Item as MyCustomModel_Factor;
                //...Do Insert 
                dbms.Database.ExecuteSqlCommand($"INSERT INTO dbo.Factor (CustomerName,CommodityID,STATUS) VALUES (N'{ROW_ITM.CustomerName}',{ROW_ITM.CommodityID},{ROW_ITM.STATUS})");


                //I think i need somthing like this ↓_________________________________
                //ObservableCollection.WHOLE_DATA_FACTOR.IsEnabled = false;
                //ObservableCollection.TheCommodityCombo_DATA.IsEnabled = false;
                //ObservableCollection.STATUS_COMBO_DATA.IsEnabled = false;

                LoadDataAgian();
                _handle = true;

                //ObservableCollection.WHOLE_DATA_FACTOR.IsEnabled = true;
                //ObservableCollection.TheCommodityCombo_DATA.IsEnabled = true;
                //ObservableCollection.STATUS_COMBO_DATA.IsEnabled = true;
                //_______________________________________________________________________
                return;
                //After this line , most stop continue ,
                //but it will go for → ObservableCollections property { get; set; } to refreshing every item , it feels Requery
            }
        }

        private void LoadDataAgian()
        {
            WHOLE_DATA_FACTOR.Clear();
            var RST = dbms.Database.SqlQuery<MyCustomModel_Factor>("SELECT * FROM Factor").ToList();
            foreach (var item in RST)
            { WHOLE_DATA_FACTOR.Add(item); }
        }
    }

Related link: Updating an ObservableCollection in WPF causes screen flicker;相关链接: 更新 WPF 中的 ObservableCollection 会导致屏幕闪烁; How can I prevent it? 我该如何预防?

Can I somehow temporarily disable WPF data binding changes? 我可以以某种方式暂时禁用 WPF 数据绑定更改吗?

Update : -The ItemsSource of the DatGrid is filled from the Factor table -The ItemsSource of the DataGridComboBoxColumn is filled with Commoditiy更新DataGridComboBoxColumnItemsSourceFactor表填充DatGridItemsSourceCommoditiy填充

When I want to Realod DataGrid's Data by "select * from Factor" I Only Reloaded the DataGrid's ItemsSource not The ItemsSource of the DataGridComboBoxColumn why DataGridComboBoxColumn's ItemsSource will refresh (as if moving on each item)当我想通过"select * from Factor"重新加载 DataGrid 的数据时,我只重新加载了 DataGrid 的 ItemsSource 而不是 DataGridComboBoxColumn 的 ItemsSource 为什么 DataGridComboBoxColumn 的 ItemsSource 会刷新(好像在每个项目上移动)

NOTE: if explanation was not good please check the video and comment in my code注意:如果解释不好,请查看我的代码中的视频和评论

Best Regards此致

Whenever you modify (add/remove) the ObservableCollection , the Target will be triggered and will start calling the getters {set; get;}每当您修改(添加/删除) ObservableCollection时,都会触发Target并开始调用 getter {set; get;} {set; get;} of the collections' items to update itself. {set; get;}集合的项目来更新自己。

If this behaviour does not meet your needs, you can do the following:如果此行为不能满足您的需求,您可以执行以下操作:

  1. Replace ObservableCollection with List .ObservableCollection替换为List

  2. Let the class implement INotifyPropertyChanged interface, like so (based on your code):让 class 实现INotifyPropertyChanged接口,如下所示(基于您的代码):

public partial class MainWindow : Window, INotifyPropertyChanged {
    // ..
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    // ..
}
  1. Whenever you want to update UI from code, you could do it explicitly using OnPropertyChanged :每当您想从代码中更新 UI 时,您都可以使用OnPropertyChanged显式执行此操作:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    //Filling DataGrid by ObservableCollection
    WHOLE_DATA_FACTOR = new List<MyCustomModel_Commodity>();
    var RST = dbms.Database.SqlQuery<MyCustomModel_Factor>("SELECT * FROM Factor").ToList();
    foreach (var item in RST)
      WHOLE_DATA_FACTOR.Add(item);
    OnPropertyChanged(nameof(WHOLE_DATA_FACTOR));
    // ..
}

NOTE that I've created a new List<MyCustomModel_Commodity>() , so to update the Target that is bound to a List , you have to give it a new reference, you could also just do WHOLE_DATA_FACTOR = WHOLE_DATA_FACTOR.ToList() just before calling OnPropertyChanged请注意,我创建了一个new List<MyCustomModel_Commodity>() ,因此要更新绑定到ListTarget ,您必须为其提供新的参考,您也可以在之前执行WHOLE_DATA_FACTOR = WHOLE_DATA_FACTOR.ToList()调用OnPropertyChanged

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM