Search code examples
wpflistviewvirtualizationlistviewitem

How to temporary disable WPF Listview virtualization?


On a custom ListView control, I am developing an "Export to Excel" small button that send all the content of the ListView to Excel. As this button is embedded inside the ListView control, I am not aware of what kind of object is listed or which columns are shown.

So my method is generic and just parse all columns headers and all rows to export data to Excel. But when parsing ListViewItem that are not visible, you get null result. A solution would be to set VirtualizingStackPanel.VirtualizationMode="False" on my ListView but it impact strongly the GUI experience when you have thousands of rows.

I am looking for a way to load all ListViewItem just for my generic exportation methods and keep Virtualization on for the GUI.

Can someone help me with that ?

Thank you.


Solution

  • I finally did come Reflection to solve my problem. Basically, I first parse all column to find the bound property name. Then do some reflection on the Items collection from the ListView to find the property value.

    Here is the code (not polished yet) :

    private void OnExportToExcel(object sender, ExecutedRoutedEventArgs args)
        {
            if (this.Items.Count > 0)
            {
                // Ensure we have a visible and selected item
                if (this.SelectedItem == null)
                    this.SelectedItem = this.Items[0];
                this.ScrollIntoView(this.SelectedItem);
    
                // Allow UI thread to update first or our selected lvi will be null if not visible
                UIHelpers.AllowUIToUpdate();
    
                List<string> columnPropertyBindingPathList = new List<string>();
                int colIndex = 0;
    
                // Get column binding path of underlying data
                foreach (GridViewColumn h in ((GridView)this.View).Columns)
                {
                    string bindingPath = "";
    
                    if (h.DisplayMemberBinding != null)
                    {
                        bindingPath = (h.DisplayMemberBinding as Binding).Path.Path;
                    }
                    else
                    {
                        ListViewItem lvi = this.ItemContainerGenerator.ContainerFromIndex(this.SelectedIndex) as ListViewItem;
                        if (lvi != null)
                        {
                            GridViewRowPresenter gvp = UIHelpers.FindVisualChild<GridViewRowPresenter>(lvi);
                            DependencyObject col = VisualTreeHelper.GetChild(gvp, colIndex);
                            TextBlock tb = col as TextBlock;
                            if (tb == null)
                            {
                                tb = UIHelpers.FindVisualChild<TextBlock>(col);
                                bindingPath = BindingOperations.GetBinding(tb, TextBlock.TextProperty).Path.Path;
                            }
                        }
                    }
    
                    colIndex++;
                    if (bindingPath != "")
                        columnPropertyBindingPathList.Add(bindingPath);
                }
    
                if (columnPropertyBindingPathList.Count > 0)
                {
                    Mouse.SetCursor(Cursors.Wait);
    
                    // Init array to export to excel
                    object[,] dataArray = new object[this.Items.Count + 1, columnPropertyBindingPathList.Count];
    
                    // Add column header
                    for (int colCnt = 0; colCnt < columnPropertyBindingPathList.Count; colCnt++)
                        dataArray[0, colCnt] = columnPropertyBindingPathList[colCnt];
    
                    // Reflection to read underlying objects
                    int rowCnt = 1;
                    foreach (object obj in this.Items)
                    {
                        for (int colCnt = 0; colCnt < columnPropertyBindingPathList.Count; colCnt++)
                            dataArray[rowCnt, colCnt] = UIHelpers.GetPropValue(columnPropertyBindingPathList[colCnt], obj);
    
                        rowCnt++;
                    }
    
                    Mouse.SetCursor(Cursors.Arrow);
    
                    // Throw event
                    RoutedEventArgs newEventArgs = new RoutedEventArgs(EmListView.ExportToExcelEvent);
                    newEventArgs.Source = dataArray;
                    RaiseEvent(newEventArgs);
                }
                else
                {
                    // Throw event
                    RoutedEventArgs newEventArgs = new RoutedEventArgs(EmListView.ExportToExcelEvent);
                    newEventArgs.Source = null;
                    RaiseEvent(newEventArgs);
                }
            }
            else
            {
                // Throw event
                RoutedEventArgs newEventArgs = new RoutedEventArgs(EmListView.ExportToExcelEvent);
                newEventArgs.Source = null;
                RaiseEvent(newEventArgs);
            }
        }
    

    UIHelpers methods :

    /// <summary>
        /// Give the UI thread the time to refresh
        /// </summary>
        public static void AllowUIToUpdate()
        {
            DispatcherFrame frame = new DispatcherFrame();
    
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
            {
                frame.Continue = false;
                return null;
            }), null);
    
            Dispatcher.PushFrame(frame);
        }
    
        /// <summary>
        /// Find a visual child type inside a DependencyObject
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                    return (T)child;
                else
                {
                    T childOfChild = FindVisualChild<T>(child);
                    if (childOfChild != null)
                        return childOfChild;
                }
            }
            return null;
        }
    
        /// <summary>
        /// Find a property value inside a object (even for multi level property)
        /// </summary>
        /// <param name="name"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static Object GetPropValue(String name, Object obj)
        {
            foreach (String part in name.Split('.'))
            {
                if (obj == null) { return null; }
    
                Type type = obj.GetType();
                PropertyInfo info = type.GetProperty(part);
    
                if (info == null) { return null; }
    
                obj = info.GetValue(obj, null);
            }
    
            return obj;
        }