Wrapping multiple server calls using IEnumerable and IEnumerator [duplicate] - c#

I have a case where I have either a gridview/listbox/any type of items control and the number of items bound to the control is massive (easily around 5000+ mark).
Each of these items needs to have various attributes loaded from various web services. Obviously, reaching out to web services to process this amount of elements all at once is out of the question.
My question is, is it possible to postpone loading until these items are actually displayed to the user? As in, the user scrolls down and although the items have been present in the collection all along, they are processed only when they are actually physically rendered.
I've seen it done before, but I can't remember where exactly. It was a situation where lots of stock quotes were in a collection bound to a gridview, but their attributes (prices etc...) were empty until they were displayed for the first time (by scrolling to their respective position).
Hopefully this made (some) sense.
Any ideas on how to pull it off?

I would try a combination of lazy loading and asynchronous loading:
Use a virtualizing list-control. Create a ViewModel for your items and fill your list with instances of the ViewModel (one per line).
In your ViewModel, make properties that have a default-value that shows the user that the data not has been loaded. The first time one of these property is accessed, trigger loading the data asynchronous and fire INotifyPropertyChanged when the real data has been received.
This will give the user a nice experience and most of the tricky work will be done through the virtualizing list (in WPF this are ListBox,ListView, DataGrid...). Hope this helped.
class LineItemVM : INotifyPropertyChanged{
bool m_loadingTriggered;
string m_name="Loading...";
string m_anotherProperty="Loading...";
public string Name{
get{
TriggerLoadIfNecessary(); // Checks if data must be loaded
return m_name;
}
}
public string AnotherProperty{
get{
TriggerLoadIfNecessary(); // Checks if data must be loaded
return m_anotherProperty;
}
}
void TriggerLoadIfNecessary(){
if(!m_loadingTriggered){
m_loadingTriggered=true;
// This block will called before your item will be displayed
// Due to the m_loadingTriggered-member it is called only once.
// Start here the asynchronous loading of the data
// In virtualizing lists, this block is only called if the item
// will be visible to the user (he scrolls to this item)
LoadAsync();
}
}
...
Additional logic
As an idea, you could also make an outer asynchrounous loading thread that loads all data in background, but has a list for items that should be loaded with higher priority. The concept is the same as in the above example, but instead of loading data from your ViewModel-item, the TriggerLoadIfNecessary-method only adds the item in the high-priority list so that the potentially visible elements are loaded first. The question which version is better suited depends on the usage of the list. If it is probable that the user uses the full list and does not navigate quickly away, this extended version is better. Otherwise the original version is probably better.

Here is an event that will notify when user scrolls into the last screen of data:
using System.Windows;
using System.Windows.Controls;
public static class ScrollViewer
{
public static readonly RoutedEvent LastPageEvent = EventManager.RegisterRoutedEvent(
"LastPage",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(ScrollViewer));
private static readonly RoutedEventArgs EventArgs = new RoutedEventArgs(LastPageEvent);
static ScrollViewer()
{
EventManager.RegisterClassHandler(
typeof(System.Windows.Controls.ScrollViewer),
System.Windows.Controls.ScrollViewer.ScrollChangedEvent,
new ScrollChangedEventHandler(OnScrollChanged));
}
public static void AddLastPageHandler(UIElement e, RoutedEventHandler handler)
{
e.AddHandler(LastPageEvent, handler);
}
public static void RemoveLastPageHandler(UIElement e, RoutedEventHandler handler)
{
e.RemoveHandler(LastPageEvent, handler);
}
private static void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ViewportHeight == 0 || e.VerticalOffset == 0)
{
return;
}
var verticalSpaceLeft = e.ExtentHeight - e.VerticalOffset;
if (verticalSpaceLeft < 2 * e.ViewportHeight)
{
var scrollViewer = (System.Windows.Controls.ScrollViewer)sender;
scrollViewer.RaiseEvent(EventArgs);
}
}
}

Related

WPF update status bar text from backgroundworker

I have a status bar on a window that I want to update with a string while a background process is running.
Specifically the user presses a button, and data is read from a file. As the file is being read, backend db calls are made to populate data objects. This is done for all lines in the file. This can take a little bit of time. This is all done in a background worker.
What I want to do is provide the user with some feel that something is happening. I would like to present the user with the name of the object that was just read in on a status line. I see BackgroundWorker provides an interger update value, but how do I do a string so that I can display it?
This is a common problem people face in WPF. I imagine you are trying to update a label from a BackgroundWorker and you are being plagued by "a different thread owns it" errors. You will probably find answers telling you to use a dispatcher to update your label. DON'T DO IT! It's unreliable and often doesn't update under heavy workloads. You should use proper binding techniques in your instance.
Hopefully this example (though not necessarily the most refactored, nor quickest to implement) can get you started on the right path, by explaining some core fundamentals or WPF.
Create an observable class that implements INotifyPropertyChanged. This is your Model. Its basically the shell of how your data will be stored (in your instance a status).
using System.ComponentModel;
public class Status_Update : INotifyPropertyChanged
{
//private property that stores value
private string status;
//public property the gets & sets value
public string Status
{
get {return status;}
set
{
if(status != value)
{
status = value;
NotifyPropertyChanged("Status");
}
}
}
//Logic to notify that property values have changed.
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Now lets move to your Control or Window's code behind constructor.
//Create a Status_Update object in your control.
public Status_Update My_Status {get;set;}
public My_Control_or_Window()
{
//Initialize the Status_Update object
MyStatus = new Status_Update(){Status=""};
InitializeComponent();
//Set the controls DataContext to itself in the constructor
DataContext=this;
}
Now in your frontend's XAML you simply bind to your control's MyStatus.Status property and it's ready for live updates from any calling thread.
<Label Content={Binding MyStatus.Status, UpdateSourceTrigger=PropertyChanged}/>
To update simply set the value of MyStatus.Status from your BackgroundWorker.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
MyStatus.Status = "Updating the first item";
Some_Task();
MyStatus.Status = "Updating the next item";
Some_Task();
}
I want to note that this example isn't the best example of MVVM, nor is it the best code structure for what you are trying to do, but it should help give you a better understanding of how binding works in WPF and how you can update things like a status label much easier with it. It takes a little more work on the front end, but saves so much time on the back end.
Best of luck.

Winforms Datagridview can't be refreshed from a delegate

I'm trying to load data from file to list and show that data immediately on Winforms' Datagridview. For that I've made the reading in another thread using Backgroundworker. The problem is, it only updates once and I can't get it to show more data. Not only that, when clicked, it tries to access element with -1 index, which of course doesn't exist, resulting in a crash.
Usually, from what I've seen, simply adding again same data to data source dataGridView1.DataSource = samelist; should work, but not in this case.
BackgroundWorker's work
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//lotsofCode...
while (readData != null)
{
fooLists.Add(readData);
//someCalculations...
worker.ReportProgress();
}
}
BackgroundWorker's progressChanged
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Invoke((MethodInvoker)delegate { UpdateGridView(); });
}
UpdateGridView Method
private void UpdateGridView()
{
if (fooLists.GetListById(1).calculatedList != null)
dataGridView1.DataSource = fooLists.GetListById(1).calculatedList;
}
Later on I've read some threads on stack, where one suggested using BindingSource as a "middleman", so now I have dataGridView1.DataSource = MyBindingSource; in the component initialization and tab1source.DataSource = fooLists.GetListById(1).calculatedList; instead of dataGridView1.DataSource. It certainly helped, as the list is now clickable as it should be, but still there are only few records on a list.
None of dataGridView1.Refresh(), dataGridView1.RefreshEdit() or dataGridView1.Update() helped, though made the list loading slightly fancier (probably due to the delay they introduced :) ).
I tried making some "protections" (semaphores, so the delegate isn't called again, while working; try-catches, though no exceptions are thrown there; data clearing before re-writing...) but the "better version" worked as poor as this one and it only darkened the code.
Am I missing a way to update the Datagridview control? Thanks in advance.
Although you didn't write it, but I think the reason that the items that you add to your dataSource are added to a collection that does not implement interface IBindingList. You probably use a simple list to hold your read data.
If your 'DataSourceimplements this interface, then after adding an item to your collection an event is raised. The class that holds theDataSource, whether it is aDataGridViewor aBindingSource` get notified about the changes in the list and update their contents accordingly.
Your solution would be to store your elements in an object of class System.ComponentModel.BindingList<T>.
Suppose the items you want to show are of class MyReadData
class MyForm : Form
{
public MyForm()
{
InitializeComponents();
this.myReadItems = new BindingList<MyReadData>();
this.MyBindingSource.DataSource = this.myReadItems;
// if not already done in InitializeComponents
this.MyDataGridView.DataSource = this.MyBindingSource;
}
private readonly BindingList<MyReadData> myReadItems;
// whenever needed, start the BackGroundWorker.
private void OnButtonReadFile_Click(object send, EventArgs e)
{
// create and start the backgroundworker
BackGroundWorkdr worker = ...
MyBackGroundWorkerParams params = ...
worker.RunWorkerAsync(params);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// I am certain the sender is my BackGroundWorker:
BackgroundWorker worker = (BackGroundWorker)sender;
MyBackGroundWorkerParams params = (MyBackGroundWorkerParams)e.Argument;
// do some work using the params
while (readData != null)
{
// some data read.
// dont't add the data to the list, just report the data that must been added to the list:
// someCalculations...
int percentProgress = ...
MyReadData dataToAddToGrid = ...
worker.ReportProgress(percentProgress, dataToAddToGrid);
}
private void bw_progressChanged(object sender, ProgressChangedEventArgs e)
{
// no need to call invoke, this is already the context of your forms thread
Debug.Assert(!This.InvokeReguired);
MyReadData dataToAdddToGrid = (MyReadData)e.UserState;
this.myReadItems.Add(dataToAddToGrid);
}
}
The main difference is that you should not let your BackgroundWorker to add data to the list of displayed data. The task of the BackGroundWorker is to read the data and to report to everyone who is interested what data has been read.
As it is the task of MyForm to display the read data, let MyForm decide which read data to display and in what format. This enhances reusage of both MyForm and MyBackGroundWorker: MyForm could display that that has been fetched in a different way, and MyBackGroundWorker could be used to inform others than MyForm to notify about read data.
Furthermore the display context of the progress changed event handler is the context of 'MyForm`, so an invoke is not needed.
You could also assign the IBindingList directly to the DataGridView, so without the use of a BindingSource. The only reason to keep a BindingSource is if you want access to the Current item, or if you want the freedom to fill your DataGridView with other items than the contents of your BindingList.
Finally: the most important part of the solution was that the items were added to an IBindingList.
System.Components.BindingList<T> is a class with limited functionality. If you want to order the rows in your DataGridView, or only show items that match some predicate, or combine items from several sources into one DataGridView, consider using Equin.ApplicationFramework.BindingListView
using Equin.ApplicationFramework;
public MyForm()
{
InitializeComponents();
this.myReadItems = new BindingListView<MyReadData>(this.components);
this.MyBindingSource.DataSource = this.myReadItems;
this.MyDataGridView.DataSource = this.MyBindingSource;
}
private readonly BindingListView<MyReadData> myReadItems;
private void bw_progressChanged(object sender, ProgressChangedEventArgs e)
{
MyReadData dataToAdddToGrid = (MyReadData)e.UserState;
this.myReadItems.Add(dataToAddToGrid);
// finished updating the list, DataGridView can be updated:
this.myReadItems.Refresh();
// this Refresh function allows you to change several items in the list
// without unnecessary intermediate updates of your BindingSource and DataGridView
}
Presto, that is all: free sorting or your columns by clicking on the column header. Consider examining their example project to see how filtering works and how to use several sources.
// Show only Brummies
this.myReadData.ApplyFilter(person => person.Address.City == "Birmingham");
// Remove the filter, show everyone again
this.myReadData.RemoveFilter();

how to handle events from variable number of sources?

I have such class (sorry about posible mistakes, i'm writing it right here.) Class is simplified for this example, it must be more complex of course.
class SP500Index {
SP500Index(List<OrderBook> stocks) {
foreach (var stock in stocks) {
stock.StockUpdated += stockUpdated; // how to handle?
}
}
}
So I have a lot of sources and I need to handle StockUpdated event from them. In handler I need to know index of stock in stocks list which raised the event. How to do that?
upd for perfomance reasons I don't want "sender look-up" instead I want index. Lookup is not trivial operation and likely involves Hashcode calculation Equals method call etc. Imagine how ofthen SP500 index changes...
This is not provided automatically.
But the StockUpdated event should look like
void StockUpdated (object sender, MyEventArgs e)
and you can cast senderto a stock and look it up in the original list. If you still need the index.
void stockUpdated (object sender, MyEventArgs e)
{
OrderBook stock = (OrderBook) sender;
....
}
It is good practice to use a signature of the form:
public delegate void CustomEventHandler(object sender, CustomEventArgs a);
In your event handler you can use sender to find out which object raised the event.
If you don't have a sender parameter then I don't think there is any (reasonable) way to find out which object raised the event.
Related
How to: Publish Events that Conform to .NET Framework Guidelines (C# Programming Guide)
If your benchmark shows that you need the index when the event occurs you can add the index as a property to OrderBook and when you add an element to the list, set this property. This value will be available to the event handler.
This will work assuming that you keep the OrderBook objects in a single List and do not do any rearranging of this original list. If you have multiple lists with each object stored on only one of those lists, then you could add an Owner property that references the list that it is stored on.
For example...
When you build List<OrderBook>:
...
OrderBook book = CreateOrderBook(...);
list.Add(book);
book.ListIndex = list.Count - 1;
// Assign Owner here if that is needed.
...
Or better, use a helper for managing the list that cares for the book keeping of the index update:
public class OrderBookManager
{
private List<OrderBook> list = new List<OrderBook>();
public void Add(OrderBook book)
{
list.Add(book);
book.ListIndex = list.Count - 1;
// Assign Owner here if that is needed.
}
// Make this read-only if you want to ensure the manager controls all updates to the list (better design) but use it this way for higher performance.
public List<OrderBook> List { get { return list; } }
}
The updated OrderBook:
public class OrderBook
{
...
public int ListIndex { get; set; }
}
And then an sample event handler using this index:
public void StockUpdated(object sender, MyEventArgs eventArgs)
{
OrderBook book = (OrderBook) sender;
//Here use book.ListIndex to access the original list element.
}
What is your reason for having that index? Everything you need should be in the object. If you use this to manipulate the original list (such as removing this item from the list) then you have the problem of recomputing the saved index of all the previously stored objects.
If you are maintaining a parallel list with other objects, then you perhaps should consider a different design.
You can define in delegate of that event sending source (which is basically the suggested guideline by Microsoft. Having object sender, in other words, like a first parameter of the delegate's signature).
After make (say) a cast and determine in some if/else the real object type.

Lazy loading of non-visible elements

I have a case where I have either a gridview/listbox/any type of items control and the number of items bound to the control is massive (easily around 5000+ mark).
Each of these items needs to have various attributes loaded from various web services. Obviously, reaching out to web services to process this amount of elements all at once is out of the question.
My question is, is it possible to postpone loading until these items are actually displayed to the user? As in, the user scrolls down and although the items have been present in the collection all along, they are processed only when they are actually physically rendered.
I've seen it done before, but I can't remember where exactly. It was a situation where lots of stock quotes were in a collection bound to a gridview, but their attributes (prices etc...) were empty until they were displayed for the first time (by scrolling to their respective position).
Hopefully this made (some) sense.
Any ideas on how to pull it off?
I would try a combination of lazy loading and asynchronous loading:
Use a virtualizing list-control. Create a ViewModel for your items and fill your list with instances of the ViewModel (one per line).
In your ViewModel, make properties that have a default-value that shows the user that the data not has been loaded. The first time one of these property is accessed, trigger loading the data asynchronous and fire INotifyPropertyChanged when the real data has been received.
This will give the user a nice experience and most of the tricky work will be done through the virtualizing list (in WPF this are ListBox,ListView, DataGrid...). Hope this helped.
class LineItemVM : INotifyPropertyChanged{
bool m_loadingTriggered;
string m_name="Loading...";
string m_anotherProperty="Loading...";
public string Name{
get{
TriggerLoadIfNecessary(); // Checks if data must be loaded
return m_name;
}
}
public string AnotherProperty{
get{
TriggerLoadIfNecessary(); // Checks if data must be loaded
return m_anotherProperty;
}
}
void TriggerLoadIfNecessary(){
if(!m_loadingTriggered){
m_loadingTriggered=true;
// This block will called before your item will be displayed
// Due to the m_loadingTriggered-member it is called only once.
// Start here the asynchronous loading of the data
// In virtualizing lists, this block is only called if the item
// will be visible to the user (he scrolls to this item)
LoadAsync();
}
}
...
Additional logic
As an idea, you could also make an outer asynchrounous loading thread that loads all data in background, but has a list for items that should be loaded with higher priority. The concept is the same as in the above example, but instead of loading data from your ViewModel-item, the TriggerLoadIfNecessary-method only adds the item in the high-priority list so that the potentially visible elements are loaded first. The question which version is better suited depends on the usage of the list. If it is probable that the user uses the full list and does not navigate quickly away, this extended version is better. Otherwise the original version is probably better.
Here is an event that will notify when user scrolls into the last screen of data:
using System.Windows;
using System.Windows.Controls;
public static class ScrollViewer
{
public static readonly RoutedEvent LastPageEvent = EventManager.RegisterRoutedEvent(
"LastPage",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(ScrollViewer));
private static readonly RoutedEventArgs EventArgs = new RoutedEventArgs(LastPageEvent);
static ScrollViewer()
{
EventManager.RegisterClassHandler(
typeof(System.Windows.Controls.ScrollViewer),
System.Windows.Controls.ScrollViewer.ScrollChangedEvent,
new ScrollChangedEventHandler(OnScrollChanged));
}
public static void AddLastPageHandler(UIElement e, RoutedEventHandler handler)
{
e.AddHandler(LastPageEvent, handler);
}
public static void RemoveLastPageHandler(UIElement e, RoutedEventHandler handler)
{
e.RemoveHandler(LastPageEvent, handler);
}
private static void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ViewportHeight == 0 || e.VerticalOffset == 0)
{
return;
}
var verticalSpaceLeft = e.ExtentHeight - e.VerticalOffset;
if (verticalSpaceLeft < 2 * e.ViewportHeight)
{
var scrollViewer = (System.Windows.Controls.ScrollViewer)sender;
scrollViewer.RaiseEvent(EventArgs);
}
}
}

c# flickering Listview on update

I have a list view that is periodically updated (every 60 seconds). It was anoying to me that i would get a flicker every time it up dated. The method being used was to clear all the items and then recreate them. I decided to instead of clearing the items I would just write directly to the cell with the new text. Is this a better approach or does anyone have a better solution.
The ListView control has a flicker issue. The problem appears to be that the control's Update overload is improperly implemented such that it acts like a Refresh. An Update should cause the control to redraw only its invalid regions whereas a Refresh redraws the control’s entire client area. So if you were to change, say, the background color of one item in the list then only that particular item should need to be repainted. Unfortunately, the ListView control seems to be of a different opinion and wants to repaint its entire surface whenever you mess with a single item… even if the item is not currently being displayed. So, anyways, you can easily suppress the flicker by rolling your own as follows:
class ListViewNF : System.Windows.Forms.ListView
{
public ListViewNF()
{
//Activate double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if(m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}
From: Geekswithblogs.net
In addition to the other replies, many controls have a [Begin|End]Update() method that you can use to reduce flickering when editing the contents - for example:
listView.BeginUpdate();
try {
// listView.Items... (lots of editing)
} finally {
listView.EndUpdate();
}
Here is my quick fix for a C# implementation that does not require subclassing the list views etc.
Uses reflection to set the DoubleBuffered Property to true in the forms constructor.
lvMessages
.GetType()
.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(lvMessages, true, null);
Update for 2021:
I got pinged on this old post with a comment and I would write this code differently now. Below is an extension method that will add a new method to a ListView to be able to set the double buffered property to true/false as required. This will then extend all list views and make it easier to call as reqired.
/// <summary>
/// Extension methods for List Views
/// </summary>
public static class ListViewExtensions
{
/// <summary>
/// Sets the double buffered property of a list view to the specified value
/// </summary>
/// <param name="listView">The List view</param>
/// <param name="doubleBuffered">Double Buffered or not</param>
public static void SetDoubleBuffered(this System.Windows.Forms.ListView listView, bool doubleBuffered = true)
{
listView
.GetType()
.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(listView, doubleBuffered, null);
}
}
If this can help, the following component solved my ListView flickering issues with .NET 3.5
[ToolboxItem(true)]
[ToolboxBitmap(typeof(ListView))]
public class ListViewDoubleBuffered : ListView
{
public ListViewDoubleBuffered()
{
this.DoubleBuffered = true;
}
}
I use it in conjonction with .BeginUpdate() and .EndUpdate() methods where I do ListView.Items manipulation.
I don't understand why this property is a protected one...even in the .NET 4.5 (maybe a security issue)
Yes, make it double buffered. It will reduce the flicker ;) http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx
Excellent question and Stormenent's answer was spot on. Here's a C++ port of his code for anyone else who might be tackling C++/CLI implementations.
#pragma once
#include "Windows.h" // For WM_ERASEBKGND
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
public ref class FlickerFreeListView : public ListView
{
public:
FlickerFreeListView()
{
//Activate double buffering
SetStyle(ControlStyles::OptimizedDoubleBuffer | ControlStyles::AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
SetStyle(ControlStyles::EnableNotifyMessage, true);
}
protected:
virtual void OnNotifyMessage(Message m) override
{
//Filter out the WM_ERASEBKGND message
if(m.Msg != WM_ERASEBKGND)
{
ListView::OnNotifyMessage(m);
}
}
};
You can use the following extension class to set the DoubleBuffered property to true:
using System.Reflection;
public static class ListViewExtensions
{
public static void SetDoubleBuffered(this ListView listView, bool value)
{
listView.GetType()
.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(listView, value);
}
}
The simplest Solution would probably be using
listView.Items.AddRange(listViewItems.ToArray());
instead of
foreach (ListViewItem listViewItem in listViewItems)
{
listView.Items.Add(listViewItem);
}
This works way better.
Simple solution
yourlistview.BeginUpdate()
//Do your update of adding and removing item from the list
yourlistview.EndUpdate()
I know this is an extremely old question and answer. However, this is the top result when searching for "C++/cli listview flicker" - despite the fact that this isn't even talking about C++. So here's the C++ version of this:
I put this in the header file for my main form, you can choose to put it elsewhere...
static void DoubleBuffer(Control^ control, bool enable) {
System::Reflection::PropertyInfo^ info = control->GetType()->
GetProperty("DoubleBuffered", System::Reflection::BindingFlags::Instance
| System::Reflection::BindingFlags::NonPublic);
info->SetValue(control, enable, nullptr);
}
If you happen to land here looking for a similar answer for managed C++, that works for me. :)
This worked best for me.
Since you are editing the cell directly, the best solution in your case would be to simply refresh/reload that particular cell/row instead of the entire table.
You could use the RedrawItems(...) method that basically repaints only the specified range of items/rows of the listview.
public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly);
Reference
This totally got rid of the full listview flicker for me.
Only the relevant item/record flickers while getting updated.
Cheers!
Try setting the double buffered property in true.
Also you could use:
this.SuspendLayout();
//update control
this.ResumeLayout(False);
this.PerformLayout();
In Winrt Windows phone 8.1 you can set the following code to fix this issue.
<ListView.ItemContainerTransitions>
<TransitionCollection/>
</ListView.ItemContainerTransitions>
For what it's worth, in my case, I simply had to add a call to
Application.EnableVisualStyles()
before running the application, like this:
private static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
Otherwise, double buffering is not enough. Maybe it was a very old project and new ones have that setting by default...
If someone would still look an answer for this, I used a timer for a slight delay and it solved the problem nicely. I wanted to highlight (change colour) for the entire row on mouse move event, but I think it would work for item replacement etc. For me listView.BeginUpdate() and listView.EndUpdate() didn't work, DoubleBuffered property also didn't work, I have googled a lot and nothing worked.
private int currentViewItemIndex;
private int lastViewItemIndex;
private void listView_MouseMove(object sender, MouseEventArgs e)
{
ListViewItem lvi = listView.GetItemAt(e.X, e.Y);
if (lvi != null && lastViewItemIndex == -1)
{
listView.Items[lvi.Index].BackColor = Color.Green;
lastViewItemIndex = lvi.Index;
}
if (lvi != null && lastViewItemIndex != -1)
{
currentViewItemIndex = lvi.Index;
listViewTimer.Start();
}
}
private void listViewTimer_Tick(object sender, EventArgs e)
{
listView.BeginUpdate();
listView.Items[lastViewItemIndex].BackColor = Colour.Transparent;
listView.Items[currentViewItemIndex].BackColor = Colour.Green;
listView.EndUpdate();
lastViewItemIndex = currentViewItemIndex;
listViewTimer.Stop();
}

Categories

Resources