简体   繁体   English

WPF Frame.Navigate 触发多个事件

[英]WPF Frame.Navigate triggering multiple events

I'm a fairly new to WPF and C#. I have a frame component on my main window and 4 buttons next to it that navigate to different views in the frame.我是 WPF 和 C# 的新手。我的主 window 上有一个框架组件,旁边有 4 个按钮,可以导航到框架中的不同视图。 Within one the the views there is a DataGrid that has a SelectionChanged event which makes an SQL call to a database that fetches records, whose data is then used to populate a list of custom objects (these relate to the selected item on the DataGrid).在一个视图中有一个 DataGrid,它有一个 SelectionChanged 事件,该事件调用 SQL 来获取记录的数据库,然后使用其数据填充自定义对象列表(这些与 DataGrid 上的选定项目相关)。 Anyways, the problem I have is that from time to time multiple calls (2 or 3) to the SelectionChanged event are being triggered at the same time for a single selection change (mouseclick) on the DataGrid.无论如何,我遇到的问题是,对于 DataGrid 上的单个选择更改(鼠标单击),有时会同时触发对 SelectionChanged 事件的多次调用(2 次或 3 次)。

The navigation button click events on the main window all look like this:主 window 上的导航按钮点击事件都是这样的:

    private void btn_MyDesk_Click(object sender, RoutedEventArgs e)
    {
        MainFrame.Navigate(new Uri("/Views/MyDeskView.xaml", UriKind.Relative));
    }

    private void btn_AllOrders_Click(object sender, RoutedEventArgs e)
    {
        MainFrame.Navigate(new Uri("/Views/AllOrdersView.xaml", UriKind.Relative));
    }

After some experiementation, I've found that the bug only happens after changing views away from the view with the DataGrid, and then changing back to it (but not always).经过一些实验后,我发现该错误仅在使用 DataGrid 将视图从视图更改为其他视图,然后再更改回该视图(但并非总是如此)后才会发生。 When the bug appears the number of calls generally corresponds to the number of times I had switched views.当错误出现时,调用次数通常对应于我切换视图的次数。 Furthermore, the bug will simply vanish if leave the program alone for a minute or two.此外,如果将程序单独放置一两分钟,该错误就会消失。 This makes me suspect that there multiple instances of the DataGrid view lingering like ghosts in memory and duplicating event calls until they are cleaned up by a garbage collector.这让我怀疑 DataGrid 视图的多个实例像 memory 中的幽灵一样挥之不去,并重复事件调用,直到它们被垃圾收集器清除。

Should I be cleaning something up each time I switch views, or am I looking in the wrong place?每次切换视图时我应该清理一些东西,还是我找错地方了?

Thank you in advance for any help.预先感谢您的任何帮助。

Edit: In answer to @Peter Moore I subscribe to the event in the DataGrid declaration within the views XAML: SelectionChanged="dtg_MyDeskOrderGrid_SelectionChanged"编辑:作为对@Peter Moore 的回答,我订阅了视图 XAML 中 DataGrid 声明中的事件:SelectionChanged="dtg_MyDeskOrderGrid_SelectionChanged"

Edit: This is the sequence that happens on a selection change in the data grid.编辑:这是在数据网格中选择更改时发生的序列。 It includes several UI changes while the SQL records for the new selection are retrieved and displayed on a second DataGrid (dtg_MyDeskOrderItems).它包括几个 UI 更改,同时检索新选择的 SQL 条记录并将其显示在第二个 DataGrid (dtg_MyDeskOrderItems) 上。 While the SQL call is being made, the relevant controls are disbaled and a semi-transparent panel (bdr_DGLoadingPanel) is moved on screen to cover them and display a loading animation. When the work is done, the work area is re-enabled and the loading panel moved off screen.当调用 SQL 时,相关控件被禁用,一个半透明面板 (bdr_DGLoadingPanel) 被移动到屏幕上以覆盖它们并显示加载 animation。当工作完成后,工作区被重新启用并且加载面板移出屏幕。 Focus is also returned to the main "order" Datagrid.焦点也返回到主“订单”Datagrid。

dtg_MyDeskOrderGrid: This is the main DataGrid showing all "Orders" dtg_MyDeskOrderGrid:这是显示所有“订单”的主要数据网格

dtg_MyDeskOrderItems: This is a secondary DataGrid that is updated to show all "Items" in the selected order. dtg_MyDeskOrderItems:这是一个辅助 DataGrid,已更新以显示所选订单中的所有“项目”。

    private void dtg_MyDeskOrderGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        CurrSelectedOrder = (Order_class)dtg_MyDeskOrderGrid.SelectedItem;
        if (CurrSelectedOrder.ItemList == null) 
        {
            if (NowWorking == false)
            {
                NowWorking = true;
                bdr_DGLoadingPanel.Margin = new Thickness(2);
                dtg_MyDeskOrderGrid.IsEnabled = false;
                bdr_FilterPanel.IsEnabled = false;
                bdr_DGLoadingPanel.Focus();
                img_LoadingCircle.RenderTransform = rt;
                img_LoadingCircle.RenderTransformOrigin = new Point(0.5, 0.5);
                da.RepeatBehavior = RepeatBehavior.Forever;
                rt.BeginAnimation(RotateTransform.AngleProperty, da);
                bdr_DGLoadingPanel.UpdateLayout();
                worker.RunWorkerAsync();
            }
        }
        else
        {
            dtg_MyDeskOrderItems.ItemsSource = null;
            dtg_MyDeskOrderItems.ItemsSource = CurrSelectedOrder.ItemList;
            dtg_MyDeskOrderItems.Items.Refresh();
        }
    }

    private void worker_DoWork(object? sender, DoWorkEventArgs e)
    {
        DatabaseConnection DBConn9 = new DatabaseConnection();
        DBConn9.FillOrderItems(CurrSelectedOrder);
    }

    private void worker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
    {
        this.Dispatcher.Invoke(() =>
        {
            dtg_MyDeskOrderItems.ItemsSource = null;
            dtg_MyDeskOrderItems.ItemsSource = CurrSelectedOrder.ItemList;
            dtg_MyDeskOrderItems.Items.Refresh();
            bdr_DGLoadingPanel.Margin = new Thickness(1000, 2, 2, 2);
            rt.BeginAnimation(RotateTransform.AngleProperty, null);
            dtg_MyDeskOrderGrid.IsEnabled = true;
            bdr_FilterPanel.IsEnabled = true;
            // The following work-around and accompanying GetDataGridCell function were used to give keyboard focus back to the datagrid to make navigation with arrow keys work again.
            // It appears keyboard focus is not returned to the Datagrid cells when using the Datagrid.focus() method.
            Keyboard.Focus(GetDataGridCell(dtg_MyDeskOrderGrid.SelectedCells[0]));
            NowWorking = false;
        });
    }

Edit... Following the advice of the commentors, I was able to fix the bug by unsubscribing from the event in the Unloaded event for the view containing the DataGrid:编辑...按照评论者的建议,我能够通过取消订阅包含 DataGrid 的视图的 Unloaded 事件中的事件来修复错误:

private void uct_MyDeskView_Unloaded(object sender, RoutedEventArgs e)
    {
        dtg_MyDeskOrderGrid.SelectionChanged -= dtg_MyDeskOrderGrid_SelectionChanged;
    }

However, I was not able to reproduce the bug using a barebones testing project.但是,我无法使用准系统测试项目重现该错误。

The UI in the original project is quite heavy, so I'm wondering if it's not the old view and events lingering in memory as this seems to fit the behavior of the bug & fix (Only occuring when I navigate away and back causing a new view to be created, multiple event triggers corresponding to the number of times I navigated away, and then finally the bug vanishing of its own accord after a moment or two).原始项目中的 UI 非常繁重,所以我想知道它是否不是旧视图和事件在 memory 中挥之不去,因为这似乎符合错误和修复的行为(仅在我导航离开和返回时发生,导致新的要创建的视图,多个事件触发器对应于我导航离开的次数,然后最终在一两分钟后错误自行消失)。

I won't be settling on this as a final solution and instead will learn about ways I can reuse instances of my views (as suggested by Bionic) instead of recreating them.我不会将此作为最终解决方案,而是了解如何重用我的视图实例(如 Bionic 所建议的)而不是重新创建它们。 The reason for this is, if the SelectionChanged event is getting multiple triggers from old view instances, then it is likely other events will suffer from the same bug.这样做的原因是,如果 SelectionChanged 事件从旧视图实例中获取多个触发器,那么其他事件很可能会遇到相同的错误。 This would be bad.这会很糟糕。

@BionicCode If you are still around, could you repost your initial comment as a solution so I can mark it answered? @BionicCode 如果您还在附近,能否将您的初始评论重新发布为解决方案,以便我将其标记为已回答?

Thank you to everyone for all the help and education.感谢大家的帮助和教育。 ^_^ ^_^

Following the advice of the commentors, I was able to initially fix the bug by unsubscribing from the event in the Unloaded event for the view containing the DataGrid:按照评论者的建议,我最初能够通过取消订阅包含 DataGrid 的视图的 Unloaded 事件中的事件来修复错误:

private void uct_MyDeskView_Unloaded(object sender, RoutedEventArgs e)
    {
        dtg_MyDeskOrderGrid.SelectionChanged -= dtg_MyDeskOrderGrid_SelectionChanged;
    }

However as this solution only disconnected the one event and didn't solve the root problem of old views lingering in the background and firing events before being cleaned up, I finally went the following code that keeps only one instance of my view in memory and reuses it.然而,由于这个解决方案只断开了一个事件,并没有解决旧视图在后台挥之不去并在被清理之前触发事件的根本问题,我最终使用了以下代码,它只在 memory 中保留了我的视图的一个实例并重用它。

private MyDeskView? currMyDeskView = null;

private void btn_MyDesk_Click(object sender, RoutedEventArgs e)
    {
        if (currMyDeskView == null)
        {
            currMyDeskView = new MyDeskView();
        }
        MainFrame.Navigate(currMyDeskView);
    }

Thank you to everyone who helped me get over this bug.感谢所有帮助我克服这个错误的人。

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

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