There is need to add to the .net windows application measurement of the responsiveness and load time for the windows forms.
What is correct way to measure winform response and load time?
I'm not sure how you would measure responsiveness but for load time you can log the StopWatch elapssed milliseconds of the successive events that are raised when a Form loads. Based on Order of Events in Windows Forms on MSDN we learn the events raised (in order) are:
Control.HandleCreated
Control.BindingContextChanged
Form.Load
Control.VisibleChanged
Form.Activated
Form.Shown
You can hookup these events in the constructor of a form and have a simple Log method to handle the data collection. Be aware that collecting the data also takes time.
public partial class Form1 : Form
{
Stopwatch timing = new Stopwatch();
// naive data collection
void Log(string stage)
{
Trace.WriteLine(String.Format("{0} = {1}", timing.ElapsedMilliseconds, stage));
}
public Form1()
{
timing.Start();
Log("Constructor Start");
InitializeComponent(); // adding all designer generated stuff
Log("Constructor - Initialized");
// hookup events
this.HandleCreated += (s, e) => Log("HandleCreated");
this.BindingContextChanged += (s,e)=> Log("BindingContextChanged");
this.Load += (s, e) => Log("Load");
this.Activated += (s, e) => Log("Activated");
this.Shown += (s, e) => Log("Shown"); ;
Log("Constructor End");
}
}
On my laptop with .Net 4.0 in Debug the timings are as follows:
0 = Constructor Start
19 = Constructor - Initialized
20 = Constructor End
22 = HandleCreated
24 = BindingContextChanged
25 = Load
30 = Activated
31 = Shown
Related
I want to add a method to the WPF Calendar control to enable it to select many dates at once, and raise the SelectedDatesChangedEvent only once at the end.
The WPF Calendar control allows you to add only one date at a time (ranges are not useful to me). However I might need to add some 1000 dates and I don't want the SelectedDatesChangedEvent handler called 1000 times because in my case it's an expensive operation.
The WPF DataGrid has a very nice feature that allows for this to be done:
public class MyDataGrid : DataGrid
{
public void ClearAndSelectMany(IEnumerable<DateTime> datesToBeSelected)
{
this.BeginUpdateSelectedItems();
...
foreach (DateTime date in datesToBeSelected)
this.SelectedDates.Add(date);
...
this.EndUpdateSelectedItems();
}
}
However the Calendar doesn't have anything like DataGrid's BeginUpdateSelectedItems(), so I'm trying to create a workaround by preventing base.OnSelectedDatesChanged() being called until it's all done:
public class MyCalendar : Calendar
{
protected override void OnSelectedDatesChanged(SelectionChangedEventArgs e)
{
if (false == this.temporaryDontReportSelectionChanged)
base.OnSelectedDatesChanged(e); // this is where I get an exception
}
public void ClearAndSelectMany(IEnumerable<DateTime> datesToBeSelected)
{
this.temporaryDontReportSelectionChanged = true;
...
foreach (DateTime date in datesToBeSelected)
SelectedDates.Add(date);
...
this.temporaryDontReportSelectionChanged = false;
OnSelectedDatesChanged(
new SelectionChangedEventArgs(
MyCalendar.SelectedDatesChangedEvent,
removedDates.ToList(),
addedDates.ToList()));
}
}
Now my problem is that I'm getting an exception when calling base.OnSelectedDatesChanged():
Unable to cast object of type
'System.EventHandler`1[System.Windows.Controls.SelectionChangedEventArgs]'
to type
'System.Windows.Controls.SelectionChangedEventHandler'.
I suppose I didn't properly create the SelectionChangedEventArgs object near the end, but I have no idea how to do it right. Any help will be appreciated.
Update: Motivated by Jamleck's question, I recreated the problem in a new project, and now have a bit more information to provide. Here's the MyCalendar class:
public class MyCalendar : System.Windows.Controls.Calendar
{
private bool temporaryDontReportSelectionChanged;
public MyCalendar()
{
// removing this line below makes my problem go away and it works ok
this.SelectedDatesChanged += MyCalendar_SelectedDatesChanged;
}
void MyCalendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
}
protected override void OnSelectedDatesChanged(SelectionChangedEventArgs e)
{
if (!temporaryDontReportSelectionChanged)
base.OnSelectedDatesChanged(e);
}
public void ClearAndSelectMany(IEnumerable<DateTime> datesToBeSelected)
{
this.temporaryDontReportSelectionChanged = true;
...
foreach (DateTime date in datesToBeSelected)
SelectedDates.Add(date);
...
this.temporaryDontReportSelectionChanged = false;
OnSelectedDatesChanged(
new SelectionChangedEventArgs(
MyCalendar.SelectedDatesChangedEvent,
removedDates.ToList(),
addedDates.ToList()));
}
}
So if I don't add an event handler to the SelectedDatesChanged event handler, everything works great, but if I do add it, then I get the InvalidCastException described above.
Hi I have used following code but sometime I didnt get the pushpin, I am using Basic key can any one please suggest me.
public MainPage()
{
InitializeComponent();
Geocode("8800 Lyra Avenue, Columbus, OH 43240", 1);
Geocode("2137 Birchwood Dr, Redmond,WA 78214,U.S.", 1);
Geocode("Santa Cruz, Duval Co., TX", 1);
}
private void Geocode(string address, int waypointIndex)
{
PlatformServices.GeocodeServiceClient geocodingService = new PlatformServices.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
geocodingService.GeocodeCompleted += new EventHandler<TestSL.PlatformServices.GeocodeCompletedEventArgs>(geocodingService_GeocodeCompleted);
PlatformServices.GeocodeRequest request = new PlatformServices.GeocodeRequest();
request.Credentials = new TestSL.PlatformServices.Credentials();
request.Credentials.ApplicationId = ((Microsoft.Maps.MapControl.ClientTokenCredentialsProvider)(MyMap.CredentialsProvider)).Token;
request.Query = address;
geocodingService.GeocodeAsync(request, waypointIndex);
}
public void geocodingService_GeocodeCompleted(object sender, TestSL.PlatformServices.GeocodeCompletedEventArgs e)
{
MapLayer myMapLayer = new MapLayer();
MyMap.Children.Add(myMapLayer);
// create a location collection class
LocationCollection myLocationColl = new LocationCollection();
var geoResult = (from r in e.Result.Results
orderby (int)r.Confidence ascending
select r).FirstOrDefault();
if (geoResult != null)
{
Pushpin myPushPin = new Pushpin();
// set it to first found location
myPushPin.Location = new Microsoft.Maps.MapControl.Location(geoResult.Locations[0].Latitude, geoResult.Locations[0].Longitude);
ToolTipService.SetToolTip(myPushPin, geoResult.DisplayName);
// add it to location collection
// which would be used to set the map's bound
myLocationColl.Add(myPushPin.Location);
// Add the drawn point to the route layer.
myMapLayer.Children.Add(myPushPin);
}
}
Sometime i get two pushpin and Sometime I didnt get anything and sometimes i get 1 or 3. can any one please tell me why this is happening.
You should geocode all your data ahead of time and store the coordinates. Trying to geocode a bunch of addresses on the fly like this will drive up the number of transactions generated by your application. When using a basic key it can be rate limited if a bunch of requests are made in a short period of time. When the request is rate limited a flag is added to the header of the response to indicate this. This is documented at the bottom half of this page: http://msdn.microsoft.com/en-us/library/ff701703.aspx
I am saving more than 100000 files in the background and all is good fast etc...I am using the tpl library and really makes threading easier.
My problem:
Been asked to add some UI to it for a user point of view.
I now have 2 listviews
"lvwInProcess" lists all the files in process
"lvwSaved" list all the files that have been saved
I cannot use "AddRange" as I am updating one at a time but the adding and removing of an item slowes me down 1000 times
The following is some simplified test code to give you an idea
private void button1_Click(object sender, EventArgs e)
{
int count = 0;
var parOpts = new ParallelOptions();
string[] files = GetFiles();
parOpts.MaxDegreeOfParallelism = Environment.ProcessorCount;
Task t1 = Task.Factory.StartNew(() =>
{
Parallel.ForEach(files, parOpts, (file, loopState) =>
{
count = Interlocked.Increment(ref count);
SaveFile(file, count);
});
});
}
private void SaveFile(string file, int count)
{
// FileProcess.Save(file);
//update listview but removing the file
// from the processing listview and add to the Saved listview
var item = new ListViewItem(Path.GetFileName(file));
lvwInProcess.Invoke(new MethodInvoker(() => lvwInProcess.Items.Add(item)));
lvwSaved.Invoke(new MethodInvoker(() => lvwSaved.Items.Remove(item)));
lvwInProcess.Invoke(new MethodInvoker(() => lvwProcessing.Refresh()));
lvwSaved.Invoke(new MethodInvoker(() => lvwSaved.Refresh()));
}
Cannot use VirtualListView as I am adding and removing
Should I use a different control?
Can the above be improved?
How would you do it?
With so many items in your ListViews, I would certainly use BeginUpdate and EndUpdate around the Add/Refresh methods. This will reduce some of the rendering time of the UI. Also is it possible to use BeginInvoke() to asynchronously update the UI.
I am trying to implement a Search as you type functionality ( like the one in the search in the default email app ) - I have a listbox with say 50 items - each item bound to a class object which has string fields ... I wish to search and display items which have the text in the search box in one of their string fields - this as the user keys in to the textbox ... tried a couple of approaches -->
1 >> Using a CollectionViewSource
- Bound a CollectionViewSource to All the items from the DB
- Bound the List Box to a CollectionViewSource
- Setting the filter property of the CollectionViewSource - to whose function which searchs for the text in the Search box in the items and set the e.Accepted - on every keyup event
- Filtering works fine but its slow with 50 items :( - guess cos of filter taking each item and checking if to set e.Accepted property to true
.... One DB call on load but seems to be a lot of processing to decide which element to disply in the filer by the CollectionViewSource
2 >> Filter # DB level
- on keyup - sending the text in the search box to the ViewModel where a function returns an ObservableCollection of objects which has the search string
- ObservableCollection is bound to the listbox
.... Not much processing # the top layer but multiple DB calls on each Keypress - still slow but just a tad faster than Approach One
Is there any other approch you would recommend ? or any suggestions to further optimize the above mentioned approaches? - any tweak to give smooth functioning for the search?
First time into mobile dev :) ... Thanx in advance :)
You can optimize the search process further by delaying the search action by x milliseconds to allow the user to continue the writing of the search criteria. This way, each new typed char will delay the search action by xxx milliseconds till the last char where the action is triggered, you'll get a better performance using just one call instead of say 10 calls. Technically speaking, you can achieve this by using a Timer class.
Here you'll find a sample source code example that shows u how to implement this.
private void txtSearchCriteria_TextChanged(object sender, TextChangedEventArgs e)
{
if (txtSearchCriteria.Text == "Search ...")
return;
if (this.deferredAction == null)
{
this.deferredAction = DeferredAction.Create(() => ApplySearchCriteria());
}
// Defer applying search criteria until time has elapsed.
this.deferredAction.Defer(TimeSpan.FromMilliseconds(250));
}
private DeferredAction deferredAction;
private class DeferredAction
{
public static DeferredAction Create(Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
return new DeferredAction(action);
}
private Timer timer;
private DeferredAction(Action action)
{
this.timer = new Timer(new TimerCallback(delegate
{
Deployment.Current.Dispatcher.BeginInvoke(action);
}));
}
public void Defer(TimeSpan delay)
{
// Fire action when time elapses (with no subsequent calls).
this.timer.Change(delay, TimeSpan.FromMilliseconds(-1));
}
}
public void ApplySearchCriteria()
{
ICollectionView dataView = this.ViewSource.View;
string criteria = this.txtSearchCriteria.Text;
string lowerCriteria = criteria.ToLower();
using (dataView.DeferRefresh())
{
Func<object, bool> filter = word =>
{
bool result = word.ToString().ToLower().StartsWith(lowerCriteria);
return result;
};
dataView.Filter = l => { return filter.Invoke(l); };
}
this.lstWords.ItemsSource = dataView;
if (this.lstWords.Items.Count != 0)
{
this.lblError.Visibility = Visibility.Collapsed;
}
else
{
this.lblError.Visibility = Visibility.Visible;
}
}
For Winform DataGridView, what I want to do is move the rows up/down by 1 cm in 1 hour. I googled about it but could not find anything which can give me a clue. Just to be more specific, let's say I have a Form open on my machine with DataGridView filled with data. And if I will look at the DataGridView after 1 hour it should be moved by 1cm up/down. The movement should be in such a way that user won't even realize and there won't be any problem while clicking/selecting cells/rows.
Can someone please point me where to start and how can I implement this?
Note: The Grid will stay as it is. Just the rows will go up/down by 1 cm in 1 hour.
Thanks,
MChicago
This code will auto-scroll the DataGridView using a timer: to move 1com in one hour, you will have to find the right value for interval by experimentation..
public partial class Form1 : Form
{
private readonly Timer tmr = new Timer();
private int start;
public Form1()
{
InitializeComponent();
tmr.Interval = 100;
tmr.Tick += scrollGrid;
tmr.Enabled = true;
List<DisplayItem> list = new List<DisplayItem>
{
new DisplayItem("Apple"),
new DisplayItem("Orange"),
new DisplayItem("Banana"),
new DisplayItem("Grape")
};
// Make a long enough list to see the scrolling
dgv.DataSource = list.Concat(list).Concat(list).ToList();
}
private void scrollGrid(object sender, EventArgs e)
{
PropertyInfo verticalOffset = dgv.GetType()
.GetProperty("VerticalOffset", BindingFlags.NonPublic |
BindingFlags.Instance);
start += 1;
verticalOffset.SetValue(this.dgv, start, null);
}
private class DisplayItem
{
public DisplayItem(string s)
{
this.Value = s;
}
public string Value { get; set; }
}
}