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

sbtkgmzw  于 2022-12-16  发布在  其他
关注(0)|答案(1)|浏览(269)

我有一个C# WPF项目,将数据从DataGrid保存到SQL Server数据库中的表中
在数据库中,我有两个表FactorCommodity,这两个表相互关联,并且是x1c 0d1x
ObservableCollection填充的数据网格ItemsSource被命名为WHOLE_DATA_FACTOR,它来自Factor
DataGridComboBoxColumnItemsSource在加载程序时仅填充一次,在RowEditEnding事件中插入Factor表后,我想重新加载数据网格的数据,
DataGridComboBoxColumn项曾经被SELECT CommodityCode, CommodityName FROM dbo.Commodity中的一个查询填充过,但是我注意到在LoadDataAgian之后,集合中的项开始刷新(就像在每个项上移动一样),这使得程序变慢,并且DataGridComboBoxColumnx 1 m15n1x再次开始填充,而我没有这样做

以下是发生的视频

MP4:https://ufile.io/p5azjp40

完整源代码和数据库https://ufile.io/uxneng9r
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>

代码隐藏

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); }
        }
    }

相关链接:Updating an ObservableCollection in WPF causes screen flicker; How can I prevent it?
Can I somehow temporarily disable WPF data binding changes?

更新:-DatGridItemsSourceFactor表填充-DataGridComboBoxColumnItemsSourceCommoditiy填充

当我想按"select * from Factor"实现DataGrid的数据时,我只重新加载了DataGrid的ItemsSource,而不是DataGridComboBoxColumn的ItemsSource为什么DataGridComboBoxColumn的ItemsSource将刷新(就像在每个项目上移动一样)
注意:如果解释不好,请检查视频和评论在我的代码
致上

cqoc49vn

cqoc49vn1#

无论何时修改(添加/删除)ObservableCollectionTarget都会被触发,并开始调用集合项的getter {set; get;}来更新自己。
如果这种行为不能满足您的需要,您可以执行以下操作:
1.将ObservableCollection替换为List
1.让类实现INotifyPropertyChanged接口,如下所示(基于您的代码):

public partial class MainWindow : Window, INotifyPropertyChanged {
    // ..
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    // ..
}

1.无论何时您想从代码更新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));
    // ..
}

注意,我已经创建了一个new List<MyCustomModel_Commodity>(),所以要更新绑定到ListTarget,必须给予它一个新的引用,也可以在调用OnPropertyChanged之前执行WHOLE_DATA_FACTOR = WHOLE_DATA_FACTOR.ToList()

相关问题