When making user control what is the proper way to update specific part of a control?
Lets say i have Image property called CornerIcon and user sets a new image to this property. Do i need to have onlyUpdateCornerIcon as a flag to update that part only? OR just do onPaint without flag checks(without flag is expensive on devices that dont have enough memory and causes sluggish behavior on update)
While the flag seems okey to do its hard to keep track of things if the control is complicated one. For example if user changes height i will need to set the height changed flag and the onlyUpdateCornerIcon as they depend on each other to show the control correctly. As a control gets complicated i end up setting 5 or 6 flags for some props...any solution around this?
Image CornerIcon{
get{return cornerIcon;}
set{
cornerIcon = value;
onlyUpdateCornerIcon = true;
Invalidate();
}
}
onPaint(..){
if(onlyUpdateCornerIcon){
//updateImageHere
return;
}
if(onlyUpdateText){
//update Text here
return;
}
//Paint all other props here
}
Related
If we've applied styling (e.CellStyle.BackColor say) to some rows via the CellFormatting event of a DataGridView, is it then possible to detect that styling at a later stage?
For example, currently we use a generic bloc of code to handle printing and exporting to Excel for any and all our DataGridViews. Until now the code hasn't catered for any styling.
So we want to add it in.
If we check the .DefaultCellStyle of the row or cell then our styling doesn't show up (it just shows as 0 or Black, which is completely wrong).
I assume that's because we've applied the styling via a CellFormatting event, instead of embedding it into the DefaultCellStyle.
Unfortunately I could not find a complete solution to your issue, only a work around.
Some experimenting with the CellFormatting event using the example from MSDN resulted in me seeing exactly what you were seeing - the BackColor was clearly being set but the CellStyle was not reflecting that. 1
The work around I found was to not use the DataGridViewCellFormattingEventArgs CellStyle property but to instead go straight to the grid. This has the downside that you now need to manually handle the case where you do not want to format the cell.
I've got some code below showing this - it is again just modifying the MSDN code:
void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// If the column is the Artist column, check the
// value.
if (this.dataGridView1.Columns[e.ColumnIndex].Name == "Artist")
{
if (e.Value != null)
{
// Check for the string "pink" in the cell.
string stringValue = (string)e.Value;
stringValue = stringValue.ToLower();
if ((stringValue.IndexOf("pink") > -1))
{
// With the commented line below we cannot access the new style
//e.CellStyle.BackColor = Color.Pink;
// With this line we can!
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = Color.Pink;
}
else
{
// With the original MSDN code the else block to reset the
// cell style was not needed.
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = dataGridView1.DefaultCellStyle.BackColor;
}
}
}
}
1. My theory is that this is similar to the confusion people have over the .Refresh() method, where the DataGridView has two very distinct views of itself, one being the rectangle painted on screen and the other being the underlying data. With the .Refresh() method you only repaint the rectangle, you do not refresh the data. I think this is like that - the CellFormatting event only formats during painting and doesn't do anything to the grid styles themselves.
A possible solution would be to add a second handler to the generic printing block of code (just before the actual printing). This handler should be attached to the CellFormatting event and only save the e.cellstyle in a temporary storage (such as a dictionary of cellstyles).
All cellstyles applied during your original cellformatting will be readable in your generic printing code without the need of adjusting specific cellformatting-events that have been tied to the datagridview.
At the end of the printing you can remove the handler again.
See also Is there a way to force a DataGridView to fire its CellFormatting event for all cells?
I am designing the architecture for a module where a search takes place.
the search takes some time, and I would like to have the UI to be responsive, so I get to delegate the retrieval of the data to a class that will do the search on a separate thread.
Then, I can think of two options :
either
1° : the search method returns a viewmodel with empty data as a return value (place holder), but once the search process is over, the viewmodel's members will be updated and the result displayed on screen thanks to data binding,
2° : the search method doesn't have any return type, but once the search process is over, an event is raised and the viewmodel with its final values is passed in the event args so it can be used by the calling code. (and eventually be consumed by a view)
Any thoughts on this one ?
EDIT: Of course, with solution 1° I'm refering to WPF databinding on the objects returned by the search-results "place-holder" objects
If you use a BackgroundWorker, the design pattern is done for you. Call your search method in the DoWork event handler, and put the results in the Results property of the DoWorkEventArgs that's passed in.
Update the UI with the results in the RunWorkerCompleted event handler (they'll be in the RunWorkerCompletedEventArgs object's Results property).
If you want to update the UI while the search is in progress, have your search method call ReportProgress and update the UI in the ProgressChanged event handler. You can put anything you like into the UserState property of the ProgressChangedEventArgs, including intermediate search results, though you do have to be careful not to pass any objects that the background thread is going to touch as it continues executing.
I'd take advantage of data binding and bind your search result presentation control to a observable collection of search results and update that collection in your ViewModel from a background worker thread. Then it doesn't required any callbacks or update messages in you code, it just works as you'd expect taking advantage of the plumbing in place in WPF.
You could also leverage Priority Binding (http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx). It provides the fast / slow options and you don't have to worry about update threads. Unless of course you are looking to add some visuals to let the user know something is happening.
Here is what I did on a recent project.
The IsBusy property is something I have in the base class for all my viewmodels. It is a boolean that a view can bind to if it wants to display some kind of waiting control like a spinner or whatever.
The _retrieveData field is just an Action that I set up during the construction of the viewmodel. I do this because the viewmodel may get its list of Cars in several different ways - so _retrieveData may have different code in it based on the constructor that was used. After _retrieveData gets the data, it will set the private backer, _cars with the data. So after _retrieveData is done, setting the public Cars to the value with the new data in _cars causes the PropertyChangedEvent which lets the view know to update itself.
So the effect is that when the view goes to get the data for the first time, it returns immediately but gets null. Then a few seconds later, it gets the actual data. During that time, the UI is responsive. And also, the IsBusy is true if the UI wants to let the user know that it is working in the background.
Not sure if this is a good way to handle it, but it is working for me so far.
public List<Car> Cars
{
get
{
if (this._cars == null)
{
base.IsBusy = true;
// Start a background thread to get the data...
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object nullObject)
{
this._retrieveData.Invoke();
this.Cars = this._cars;
base.IsBusy = false;
}));
// While waiting on the background thread, return null for now. When the background thread
// completes, the setter will raise OnPropertyChanged and the view will know its time to bind...
return this._cars;
}
return this._cars;
}
set
{
this._cars = value;
base.OnPropertyChanged("Cars");
}
}
I have a complex WPF control that draws a lot of primitives in its OnRender (it's sort of like a map). When a small portion of it changes, I'd only like to re-issue render commands for the affected elements, instead of running the entire OnRender over. While I'm fine with my OnRender function's performance on a resize or whatever, it's not fast enough for mouse hover-based highlighting of primitives.
Currently the only way I know how to force a screen update is to call InvalidateVisual(). No way to send in a dirty rect region to invalidate.
Is the lowest granularity of WPF screen composition the UI element? Will I need to do my renders of primitives into an intermediate target and then have that use InvalidateVisual() to update to the screen?
When you want to write WPF custom/composite controls, you should try to avoid overriding OnRender as possible especially if you plan to invalidate portions of it. It's much easier to use AddVisualChild + override VisualChildrenCount + override GetVisualChild + override Measure & Arrange like this (pseudo code with 2 children):
private void BuildMyControls()
{
AddVisualChild(subControl1);
AddVisualChild(subControl2);
}
protected override int VisualChildrenCount
{
get
{
return 2;
}
}
protected override Visual GetVisualChild(int index)
{
if (index == 0) return subControl1;
if (index == 1) return subControl2;
return null; // should never be called in fact...
}
protected override Size MeasureCore(Size availableSize)
{
base.Measure...
BuildMyControls();
.. measure them, probably call subControlX.Measure(...);
}
protected override void ArrangeCore(Rect finalRect)
{
base.ArrangeCore(finalRect);
... arrange them, probably call subControlX.Arrange
}
With this kind of code, you can invalidate just one portion, with something like subControlX.InvalidateXXX();
WPF doesn't work quite like that, so you can't invalidate regions. However, there are some optimizations that can be made. There is a Measure, Arrange, and then Render pass. If a control moves but what actually renders doesn't change then you can tell WPF to do only the arrange pass. You can trigger these invalidations off of dependency property value changes with FrameworkPropertyMetadata and FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx).
You shouldn't be using InvalidateVisual() unless the size of your control changes, as it causes a fairly expensive re-layout of your UI.
WPF is a retained drawing system. That means OnRender() might better be called AccumulateDrawingObjects(). It's actually accumulating a tree of live drawing objects, which only needs to happen once per layout. It then uses these objects to draw your UI whenever it needs to. To change how a portion of your UI looks without re-layout, some objects (like DrawingGroup, RenderTargetBitmap, and WriteableBitmap) can be updated after OnRender(), anytime you like.
To update a portion of your UI later, wrap those commands in a DrawingGroup and put that object into the DrawingContext. Then you can Open() and update it whenever you want, and WPF will automatically repaint that portion of the UI.
This is what it looks like:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}
I am trying to mimic the functionality of the address book in Outlook
So basically a user starts typing in some text in an edit control and a matching ListView Item is selected
private void txtSearchText_TextChanged(object sender, EventArgs e)
{
ListViewItem lvi =
this.listViewContacts.FindItemWithText(this.txtSearchText.Text,true, 0);
if (lvi != null)
{
listViewContacts.Items[lvi.Index].Selected = true;
listViewContacts.Select();
}
}
The problem with this is once the listview item gets selected the user cant keep typing into the text Box. Basically I want a way to highlight an item in the listview while still keeping the focus on the edit control
This is WINFORMS 2.0
Manually setting ListViewItem.BackColor is not good a solution, especially if you want the item to get the selected state, because it only works on unselected items. So you had to take care of several situations to make it look right in all cases (really select the item as soon as the ListView gets focus, undo the color changes, and so on...)
It seems the only good way is to use Ownerdraw or an extended ListView like ObjectListView.
I was looking for the same and I still hope for a better/smarter solution, or at least a nice and short Ownerdraw implementation.
Update
I found a better solution for me: I now use a DataGridView for the same purpose (which also has other advantages in my case, since the data comes from a db anyway, but it would also work without db). There the selection bar doesn't change color when loosing focus. You can try a few properties to make it look like a ListView:
dgv.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None;
dgv.ColumnHeadersVisible = false;
dgv.MultiSelect = false;
dgv.ReadOnly = true;
dgv.RowHeadersVisible = false;
dgv.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
dgv.StandardTab = true;
ok never mind it's doable by just manipulating the background colour of the selected item
I have set up a dialog with several tabs. One of these contains twenty combo boxes, each with over 100 items, added like this :
foreach (var x in collection)
{
string text = FormatItem (x);
combo.Items.Add (text);
}
so there is nothing fancy at all with the items. They are plain strings and the combo boxes get filled when the dialog is created. This happens almost instantenously.
However, when the user clicks on the tab containing all these combo boxes for the very first time, the GUI freezes for several seconds (and I am running on a really beefy machine).
I loaded the symbols for System.Windows.Forms and tried to break into the debugger while the program is stuck. What I have discovered is a stack trace with the following calls:
System.Windows.Forms.Control.CreateHandle()
System.Windows.Forms.ComboBox.CreateHandle()
System.Windows.Forms.Control.CreateControl(...) x 3
System.Windows.Forms.Control.SetVisibleCore(true)
System.Windows.Forms.TabPage.Visible.set(true)
which results in plenty of native transitions, WndProc calls, etc. I suppose this happens for every single item in every combo box. Phew.
Obviously, I cannot optimize WinForms. But maybe I can take some actions in order to avoid all this hell getting lose on my poor GUI? Any ideas?
Nota bene:
I've no event handlers attached on the combo boxes which could be called when the controls get created for real.
If I try to access the Handle property of the combo boxes just after having created and populated the form, I pay the penalty at that moment, rather than when the tab becomes visible for the first time. But having to wait several seconds when creating the form is not acceptable either. I really want to get rid of the long set up time.
The idea of applying BeginUpdate and EndUpdate does not apply here: these should be used to prevent the control from repainting when its items list gets filled. But in my case, the problem happens well after the control has been set up.
What you're saying is not consistent with anything I ever observed... :s
But have you tried using .BeginUpdate / .EndUpdate ?
Another thing you coud try is not populate the boxes until needed. Delay it until the box gets focus for example... (If you trap the dropdown event some user might be annoyed that the up/down arrow keys won't work.)
Everything I tried failed up to now to speed up the first display of the tab containing all the combo boxes. Data binding didn't help either.
I finally decided to fix the issue by doing a trick, similar to what danbystrom proposed, i.e. only populate the Items collection when the focus first arrives on a combo. This still produces a noticeable delay, the time all items get created (within a BeginUpdate and EndUpdate pair of method calls), but it is tolerable (approx. 200ms versus several seconds in my original scenario).
Instead of iteration your collections, would't setting the ComboBox.DataSource be a viable, and much faster alternative?
comboBox1.DataSource = myCollection1;
comboBox2.DataSource = myCollection2;
comboBox3.DataSource = myCollection3;
// and so on...
Here is a more complete example:
public class Entity
{
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<Entity> list = new List<Entity>
{
new Entity {Title = "Item1"},
new Entity {Title = "Item2"},
new Entity {Title = "Item3"}
};
comboBox1.DataSource = list;
}
I've just been coming up against this same problem, where populating a combobox with around 4000k items was unacceptably slow.
I was filling the combo in the OnLoad event handler of a form, however, when I shifted this code to constructor, after InitializeComponent(), there was no delay at all.
I guess doing this operation in the OnLoad was causing a redraw of the combo to fire, hence the delay? Anyway, just thought I'd add this in case it's of use to anyone else in this situation.
Lots of controls on a form can be a problem. I once had a form that dynamically created between 50-100 textbox controls. It was slow to load.
We solved that problem by using a datagrid instead. It's a control that is optimized for lots of data. I don't know what your exact requirements are, but it might work.