I would like to update some or all of my listview's items and subitems contents with a timer (1 second refresh) But the listview flicker each one second. Sometimes the subitems are lost during redrawing. Because my listview contains data that is to be likely changed anytime, I use a timer.
Code:
I put this function in the timer's Tick method
void Refresh()
{
foreach(string s in lsttring)
{
lv.items.add(s);
lv.items[i].subitems.add(i);
}
}
I expect only items content (item text and subitem text) that are changed will be changed not the whole listview along with the timer tick.
The ListView control supports double buffering, it maps the DoubleBuffered property to the native control's LVS_EX_DOUBLEBUFFER style flag. It is quite effective but you can't get to it directly since it is a protected property. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing the old one.
using System;
using System.Windows.Forms;
class BufferedListView : ListView {
public BufferedListView() {
this.DoubleBuffered = true;
}
}
Try this:
void Refresh()
{
lv.BeginUpdate();
foreach(string s in lsttring)
{
lv.items.add(s);
lv.items[i].subitems.add(i);
}
lv.EndUpdate();
}
In this manner you update all the items and listview will be refreshed only at the end of this operation.
From Microsoft:
...if you want to add items one at a time using the Add method of the
ListView.ListViewItemCollection class, you can use the BeginUpdate
method to prevent the control from repainting the ListView every time
that an item is added. When you have completed the task of adding
items to the control, call the EndUpdate method to enable the ListView
to repaint. This way of adding items can prevent flickered drawing of
the ListView when lots of items are being added to the control.
Related
Using a worker thread, I am adding items to a ListView, however to do this I have implemented a delegate in the form as shown below in the code sample.
Problem:
When the ListView.Items.add() line is executed, nothing is added to the ListView
What I have tried:
I added a button, when clicked, it successfully adds a ListViewItem to the ListView object, as it should. (this is before the worker thread start, for testing/debugging)
I have also tried swapping the ListView to a RichEdit in case I had a bug in the code, but the items successfully display in the RichEdit, thus I added another ListView incase the first/previous one was buggy, and without renaming the ListView, I tried adding the items, but without success.
What happens in the Delegate:
Nothing, after the clear() method, the 4 columns each with headers disappear and nothing is added to the list view
Conclusion:
My only conclusion is that the delegate is not compatible with the ListView, since I was able to add the items to a RichEdit, but not to a ListView.
Is this a bug? Am I doing something wrong?
code sample
void connected_add_device()
{
string[] t = { "123", "-1", "sa.d.sdf.s.fg", "sda-f-sd-fgds-gf" };
if (display_connected_Devices.InvokeRequired)
display_connected_Devices.BeginInvoke((MethodInvoker)delegate ()
{
display_connected_Devices.Clear();
display_connected_Devices.Items.Add(new ListViewItem(t));
}
);
else
{
display_connected_Devices.Clear();
display_connected_Devices.Items.Add(new ListViewItem(t));
}
}
For what it is worth, I had a bad GIT commit. After everthing is fixed, this ListView (which before the GIT issue, had successfuly displayed the items) now doesn't display the items anymore.
Use display_connected_Devices.Items.Clear(); instead. Because display_connected_Devices.Clear(); will clear, like you noticed, the columns aswell.
I would like to update some or all of my listview's items and subitems contents with a timer (1 second refresh) But the listview flicker each one second. Sometimes the subitems are lost during redrawing. Because my listview contains data that is to be likely changed anytime, I use a timer.
Code:
I put this function in the timer's Tick method
void Refresh()
{
foreach(string s in lsttring)
{
lv.items.add(s);
lv.items[i].subitems.add(i);
}
}
I expect only items content (item text and subitem text) that are changed will be changed not the whole listview along with the timer tick.
The ListView control supports double buffering, it maps the DoubleBuffered property to the native control's LVS_EX_DOUBLEBUFFER style flag. It is quite effective but you can't get to it directly since it is a protected property. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing the old one.
using System;
using System.Windows.Forms;
class BufferedListView : ListView {
public BufferedListView() {
this.DoubleBuffered = true;
}
}
Try this:
void Refresh()
{
lv.BeginUpdate();
foreach(string s in lsttring)
{
lv.items.add(s);
lv.items[i].subitems.add(i);
}
lv.EndUpdate();
}
In this manner you update all the items and listview will be refreshed only at the end of this operation.
From Microsoft:
...if you want to add items one at a time using the Add method of the
ListView.ListViewItemCollection class, you can use the BeginUpdate
method to prevent the control from repainting the ListView every time
that an item is added. When you have completed the task of adding
items to the control, call the EndUpdate method to enable the ListView
to repaint. This way of adding items can prevent flickered drawing of
the ListView when lots of items are being added to the control.
I have a custom control that has a refresh method, something similar to this:
public class MyControl : Canvas
{
// Dependency property for "data" used to draw the control here
public void Refresh()
{
Children.Clear();
// Using data, draw the control
Children.Add(new Line(...));
Children.Add(new Rectangle(...));
// etc.
}
}
Right now, I have to call Refresh() manually each time I want the look of the control to update. My dependency properties are set up for FrameworkPropertyMetadataOptions.AffectsArrange, so WPF knows that modifying the properties will affect the arrangement of the control and that it should be redrawn. So here's the question:
What does WPF use to tell a custom control that it should be redrawn? Is it an event, or an override, and how should it be used? I've tried handling various events and overrides and nothing seems to work. So, what's the correct way to do this? I want to replace / wrap the Refresh() method above in something "automatic" that WPF will handle automatically.
I've been trying to fix this issue and it seems to be a problem from the .Net framework. anyway, I have a listview which contains 5000+ items and a button used to do further processing to the selected items in the listview. now clicking this button should also set "listview.HeaderStyle" property of the listview to "ColumnHeaderStyle.Nonclickable".
now when I do that. the program hangs for like 10 seconds then continues its work. I have no idea what is causing and why this is happening. I hope you guys have a solution for this.
any ideas?
I've found a basic solution for now, all I need is to set
myListview.ColumnClick += new ColumnClickEventHandler(delegate{});
Now I don't need to change "listview.HeaderStyle" property anymore.
basically I was disabling the ColumnClick event from the HeaderStyle property and that's all. so instead of setting the HeaderStyle to nonClickable. I just remove/change the function inside event handler.
You can solve this problem using BackgroundWorker MSDN.
Note : Even using this solution your form will hang for some time. Because you have 5000+ items to be bind to the list, which will block your UI thread, hence winform will hang. But your listview.HeaderStyle will get modified.
Try this
On button click, you directly change the propert of listview.HeaderStyle to ColumnHeaderStyle.Nonclickable. Then call RunWorkerAsync of BackgroundWorker.
On DoWork event handler you do the processing, and once done bind data to list view. To do this you will need to add following extension class to your project.
Extension Class
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
if (control.InvokeRequired) control.Invoke(new MethodInvoker(action), null);
else action.Invoke();
}
}
Using this you can bind data to listview
listview.Invoke(() => ( listview.DataSource = dataSource; });
Hope this works for you.
Let's say I have 2 forms. In Form1 I have a timer, which counts (count++) and in Form2 I have a text box (textBox1) which shows how much the timer has counted. Now I want to know how would I show the integer 'count' in 'textBox1' without any user interference (clicking buttons) or in other words, how would I make the data in the text box auto refresh (without using Form2 form = new Form2(); form.Show();). I want 2 separate windows, in one a running timer and in the other a textbox displaying how much the timer has counted and constantly updating (with the help of the timer, I presume).
One way is by creating a public event and registering for that event from the other form.
Simplest way: Expose a public property on Form2. In the setter for the property, set the value of the textbox. I believe the timer event fires on the UI thread, so you shouldn't have any thread safety issues. If you do, you'll have to go back to the public event approach that Brian mentioned above.
Keep in mind that you may also have to do a DoEvents() to get the UI to actually update to the user. Also keep in mind that this kind of update inherently slows down the processing of your application.
public int TimerValue
{
set
{
this.txtTextBox.Text = string.Format("{0:0000}", value);
}
}
You could have a singleton/static class that holds all data that are relevant to all forms, exposed as properties. All forms could write and read these properties. Additionally it fires Events that the forms can subscribe to when the properties change (in case you need live updates).
Just make your timer a public event so it can be referenced on another form.
Implement INotifyPropertyChanged on the timer form, and define a public property representing the count:
private int count;
public int Count
{
get { return count; }
set
{
if (count != value)
{
count = value;
OnPropertyChanged("Count");
}
}
}
Then, on the textbox form, databind your textbox Text value to the Count property of the timer form.
textbox.DataBindings.Add("Text", timerForm, "Count");