Frequently writing to WPF Textbox crashes application - c#

I have an application with several tabs. One of these tabs is a "Report" tab that acts as an output console. When I first run the application, I read a file and write maybe 2000 lines to the Textbox. This works fine when a tab that is not the "Report" tab is active.
However, should I be viewing the "Report" tab (with the Textbox) whilst it is having a lot of data written to it, the application writes one or two lines of data and then will not respond. The whole application crashes.
The code I am using to update is quite simple:
string timestamp = DateTime.Now.ToString("H:mm:ss");
string output = this.textbox_report_output.Text;
output += timestamp + ": " + message + "\n";
this.textbox_report_output.Text = output;
this.textbox_report_output.ScrollToEnd();
I have had very bad performance with WPF's Textbox when putting it under this kind of stress. Is there anything I can do?

I have had very bad performance with WPF's Textbox when putting it under this kind of stress. Is there anything I can do?
Yes, you can write WPF properly instead of pretending that it is Windows Forms.
In WPF, we use data binding to update the UI instead of procedural code. I suggest that you have a good read of the Data Binding Overview page on MSDN to get you started, but it basically goes like this:
Create a string property and implement the INotifyPropertyChanged interface:
public string Text { get; set; } // Implement the INotifyPropertyChanged interface
Data bind it to the TextBox.Text property:
<TextBox Text="{Binding Text}" />
Set the value:
Text = "Some value";
Use the Dispatcher to run the UI updates while you are still executing other code:
Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate { Text = "Some value"; });

Obviously there is something wrong beyond what I'm about to say but since you are using WPF, there's a better way to update the UI than you are using. Instead of pushing the updates like you are doing, you could let the WPF binding system take care of the logistics of updating the UI.
This way, the updates won't flood the UI thread. To do that, either create a DependencyProperty (or implement INotifyProperty changed) and then bind the text box to the property.
That said, if the application is crashing you're probably doing something else wrong. Maybe you're leaking memory and your getting an OutOfMemoryException?! Or perhaps some threading issue?!

I would agree with the other two responses that data binding is the best option for wpf textbox input and output. But, in some cases you do not have that luxury, especially, when the data you need is on a view and control you are accessing are via remoting and formatting the UI output is important.
If you are setting the Textbox.Text field in the OnTextChanged event (while in Japanese or Chinese and your entry is full width), then there is a high chance that it will crash your system. Try, instead to capture the LostFocus event, then make corrections there. Then, it us safe to set the value in the Textbox.Text field. Also, you can intercept the KeyDown event and remove non-numerical keys except the decimal point.

Related

Updating The .Text of the TrayNotifyIcon

I'm currently using the .Text of the TrayNotifyIcon to display a statusdisplay when the user has the mouse over it (for a percentual completion of a process)
Thus I just set: TrayNotifyIcon.Text to the appropriate % of completion.
Example (the following code is part of a code I use where I create a new thread which sets in the subprocess variable if it is completed or not and also how many % completion are. The code below shall display as the TrayNotifyIcon.Text how many % of the subprocess are completed with updates every second):
while (subprocess.NotCompleted)
{
TrayNotifyIcon.Text = "TextToUpdateTo....." + subprocess.percent.ToString() + "% completion";
Thread.Sleep(1000);
}
Now I've seen that it only updates the display whenever I move the mouse and does not update it as soon as I set .Text appropriately.
Thus my quesiton is is there any way to make it so that I can tell the system to update the text that is being displayed?
The system displays the hint text when the mouse is hovered over the notification icon. This text is not expected to be dynamic. The display of the text is handled by the system. When it wants to display the hint, it queries the icon for the text, and then displays it. It will not go back and check if the text has been changed, and there is no mechanism to inform the system that the text has been changed and should be updated. This is a feature of the shell (Shell_NotifyIcon) rather than anything in the .net libraries.
So, using TrayNotifyIcon in its vanilla form, there is nothing you can do to change this behaviour. If you really want dynamic update of hint text you will have to suppress the system drawn hint (by setting Text to an empty string) and drawing your own hint window. You'd need to detect the mouse hovering over the icon, and also detect when it leaves the icon.
Frankly, I don't think that this is really a very good piece of UX design. If I were you I would find a different way to let the user receive this feedback.

Apply Input Mask for a ToolStripComboBox C#

I was just wondering if anyone has come across how to apply an input mask for a Tool Strip Combo Box in C#?
My drop down box gets populated over time with IP addresses, and I would like to restrict what the users are able to write in (Ex: can only input 3 '.'s, only numbers, etc).
So if anyone is able to help out I would really appreciate it!
Thanks in advance.
Edit
My design has changed so I now need to have a ToolStripComboBox
You could try catching the KeyUp event, then check that the input is valid. If not revert it to the last valid input. You would probably want to do something similar with the Validating event (make sure CausesValidation is true).
Another option would be to create a MaskedTextBox and place it so it covers the text box portion of the drop down menu. You would then need to wire up the events so the two form controls remained synced.
You could also look into the ErrorProvider class.
There are a couple of other ways (like a timer which runs ever .3 seconds), but those are usually performance hogs or difficult to maintain.
Update for regular expression comment:
If I was to do this I might use a regular expression or I might manually parse the string.
Either way the KeyUp and Validating events is where I would check the validation of the control. The KeyUp event gives me the option to check as they type while the Validating event allows me to validate when the control loses focus. Which you use will depend on what you want the user experience to be.
If you do not use the KeyUp event to validate, you could add a timer which runs 5 seconds after the last key press. This way the control would not have to lose focus for the error to show.
Update for edited question and comment:
You could not use Format event as your question was on how to format user input, not how things are added to the list. As such that solution does not work with ToolStripComboBox or with ComboBox.
After reading the documentation for ToolStripControlHost, you might be able to cast ToolStripComboBox to ComboBox. If not then you could use the ToolStripControlHost to place the ComboBox onto your form. - This is incorrect or unnecessary, please see update below the quote.
ToolStripControlHost is the abstract base class for ToolStripComboBox, ToolStripTextBox, and ToolStripProgressBar. ToolStripControlHost can host other controls, including custom controls, in two ways:
Construct a ToolStripControlHost with a class that derives from Control. To fully access the hosted control and properties, you must cast the Control property back to the actual class it represents.
Extend ToolStripControlHost, and in the inherited class's default constructor, call the base class constructor passing a class that derives from Control. This option lets you wrap common control methods and properties for easy access in a ToolStrip.
Use the ToolStripControlHost class to host your customized controls or any other Windows Forms control.
To customize a ToolStripItem, derive from ToolStripControlHost and create a custom implementation. You can override methods such as OnSubscribeControlEvents to handle events raised by the hosted controls, and you can put custom functionality into properties to enhance the hosted control.
Update:
According to the ToolStripComboBox documentation you can access the underlying ComboBox through ToolStripComboBox's ComboBox property.
This is why I usually read the documentation on a control before I use it. I might not understand it, but at least I will have an idea what to look for. :)
You should create Format event like this:
private void comboBox1_Format(object sender, ListControlConvertEventArgs e)
{
e.Value = GetFullIpFormat((string)e.Value);
}
And here is code for formating values:
string GetFullIpFormat(string value)
{
string[] ip = new string[4];
for (int i = 0; i < ip.Length; i++)
{
ip[i] = GetIpPart(i, value);
}
return string.Format("{0:###}.{1:###}.{2:###}.{3:###}", ip[0], ip[1], ip[2], ip[3]);
}
string GetIpPart(int partNumber, string ip)
{
string result = "000";
int iLen = 3;
ip = ip.Replace(".", "");
int iStart = partNumber * iLen;
if (ip.Length > iStart)
{
result = ip.Substring(iStart);
if (result.Length > iLen)
{
result = result.Substring(0, iLen);
}
}
return result;
}
This will do formating for you.
Alternativly you can check input on same event for numbers.
This will do the job for you, happy coding! :)

Put the selected text in a variable of my application

In my application, I have a textbox. I want to be able to copy text from a webpage or document open elsewhere in Windows by simply highlighting the text and pressing a global hotkey - then have that text appear in the textbox.
What I am stuck on is the selection part: I'm looking for a way to use the selected text as a variable within my WPF application, but I can't seem to get access to it in a way that makes sense.
Right now, I'm working with the clipboard and Clipboard.GetText() to get my variable :
private void OnHotKeyHandler(HotKey hotKey) {
if (Clipboard.ContainsData(DataFormats.Text))
tb_number.Text = Clipboard.GetText();
}
But i'm trying to avoid a tedious CTRL-C each time i want to get this value and work with selected text, rather than copied text. Any ideas how I can do this on-select rather than by accessing the clipboard?
I beleive the question is much clearer now. Thanks for the editing.
I dont think you can modify the clipboard functionality thru a .NET application in an easy way. But you may get what you need with a third party application for windows called autohotkey.
http://www.autohotkey.com/docs/Tutorial.htm#Send
https://superuser.com/questions/166270/change-ctrl-x-c-and-v-hotkeys-in-windows-to-different-keys

Retrieve activity name

I'm working in a WPF project and I need one requirement.
This is my main window:
http://img210.imageshack.us/img210/9752/rule.jpg
So, I want that when I drag and drop a custom activity (aggregate Functions), display his name in Compute TextBox... Is there some way to do it????
For example:
1) Drag and drop MaxActivity
2) Display in TextBox Compute: MaxActivity
3) Drag and drop SumActivity
4) Display in TextBox Compute: MaxActivity + SumActivity
5) Drag and Drop MaxActivity
6) Display in TextBox Compute: MaxActivity + SumActivity + MaxActivity1
Thanks a lot!
I would strongly suggest you avoid this design, as however you end up implementing it will introduce brittleness to your code without any benefit (I can already see you are aggregating max and sums, why tell me again?)
If you go ahead, get rid of that "Compute" TextBox. TextBoxes = enter text. You are just rephrasing what is already in the design surface. It makes no sense to allow me to change the text you generate. Use a TextBlock or Label.
Do not, I repeat, not save this in your Activity. This is all UI tasks and should not be saving this information in your Activity configuration
If you have this
public sealed class MyActivity : Activity
{
public string Compute {get;set;} // NO!
and this
<!-- NO! -->
<Label>Compute</Label><TextBox Text="{Binding ModelItem.Compute}"/>
you're probably doing this wrong.
So, how do you do it?
You can parse out your ModelItem and generate this string by listening to changes in the ModelItem and constructing this string every time. Note, your ModelItem will not be set when the constructor is called, so you are going to have to listen to changes to the ActivityDesigner.ModelItem property. There is no event for this, so you will have to know how to listen to changes in a DependencyProperty.
Once you are listening to changes in your ModelItem, whenever a change happens, you can walk down your ModelItem (which is hard) or just get the Activity from the ModelItem and examine it.
var child = ModelItem.Properties["Child"].Value.GetCurrentValue();
if(child == null)
computeTextBox.Text = "Please add some computation stuff.";
else
// hard stuff goes here...
Walking down the path from your Activity's child to whatever activities are held inside can be treacherous, as you can't assume your user has thrown in a Sequence rather than a single MaxActivity, or that they don't have seven nested Sequences, etc etc. So you have to have a pretty complex and brittle set of if else logic to try and parse out what is contained below. And if you start changing properties of the Activity tree outside of the ModelItem you're going to get out of sync with the UI.
Now, after reading that, go back to point 1. I listed in this answer and follow my advice. Drop the idea completely.

How not to lose binding source updates?

Suppose I have a modal dialog with a textbox and OK/Cancel buttons. And it is built on MVVM - i.e. it has a ViewModel object with a string property that the textbox is bound to.
Say, I enter some text in the textbox and then grab my mouse and click "OK". Everything works fine: at the moment of click, the textbox loses focus, which causes the binding engine to update the ViewModel's property. I get my data, everybody's happy.
Now suppose I don't use my mouse. Instead, I just hit Enter on the keyboard. This also causes the "OK" button to "click", since it is marked as IsDefault="True". But guess what? The textbox doesn not lose focus in this case, and therefore, the binding engine remains innocently ignorant, and I don't get my data. Dang!
Another variation of the same scenario: suppose I have a data entry form right in the main window, enter some data into it, and then hit Ctrl+S for "Save". Guess what? My latest entry doesn't get saved!
This may be somewhat remedied by using UpdateSourceTrigger=PropertyChanged, but that is not always possible.
One obvious case would be the use of StringFormat with binding - the text keeps jumping back into "formatted" state as I'm trying to enter it.
And another case, which I have encountered myself, is when I have some time-consuming processing in the viewmodel's property setter, and I only want to perform it when the user is "done" entering text.
This seems like an eternal problem: I remember trying to solve it systematically from ages ago, ever since I've started working with interactive interfaces, but I've never quite succeeded. In the past, I always ended up using some sort of hacks - like, say, adding an "EnsureDataSaved" method to every "presenter" (as in "MVP") and calling it at "critical" points, or something like that...
But with all the cool technologies, as well as empty hype, of WPF, I expected they'd come up with some good solution.
At critical points, you can force the binding to push through to your view model:
var textBox = Keyboard.FocusedElement as TextBox;
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
Edit:
OK, since you don't want hacks we have to face the ugly truth:
In order to implement a clean view, the properties exposed by your view model should be friendly to frequent binding updates.
An analogy we can use is a text editor. If the application was a giant text box bound to a file on disk, every keystroke would result in writing the whole file. Even the concept of saving is not needed. That's perversely correct but terribly inefficient. We all immediately see that the view model needs to expose a buffer for the view to bind to and this re-introduces the concept of save and forces state handling in our view model.
Yet, we see this is still not efficient enough. For even medium-sized files the overhead of updating the whole-file buffer on every keystroke becomes unbearable. Next we expose commands in our view model to efficiently manipulate the buffer, never actually exchanging the whole buffer with the view.
So we conclude that in order to achieve efficiency and responsiveness with pure MVVM, we need to expose an efficient view model. That means that all text boxes can be bound through to properties with no ill effects. But, it also means that you have to push state down into the view model to handle that. And that's OK because a view model is not the model; it's job is it to handle the needs of the view.
It's true that we can rapidly prototype user interfaces by taking advantage of shortcuts like binding on focus changes. But binding on focus changes can have negative consequences in real applications and if so then we should simply not use it.
What is the alternative? Expose a property friendly to frequent updates. Call it the same thing as the old inefficient property was called. Implement your fast property using the slow property with logic that depends on the state of the view model. The view model gets the save command. It knows whether the fast property has been pushed through to the slow property. It can decide if when and where the slow property will be synched to the model.
But you say, haven't we just moved the hack from the view to the view model? No, we have lost some elegance and simplicity, but go back to the text editor analogy. We have to solve the problem, and it is the view model's job to solve it.
If we want to use pure MVVM and we want efficiency and responsiveness, then lame heuristics like let's avoid updating the binding source until the element loses focus won't help. They introduce as many problems as they solve. In that case, we should let the view model do its job, even if means adding complexity.
Assuming we accept it, how can we manage the complexity? We can implement a generic wrapper utility class to buffer the slow property and allow the view model to hook its get and set methods. Our utility class can auto-register for save command events to reduce the amount of boilerplate code in our view model.
If we do it right, then all the parts of the view model that were fast enough to be used with property changed binding will all still be the same, and the others that were worthy of asking the question "Is this property too slow?" will have a small amount of code to address the issue, and the view is none the wiser.
This is a tricky one and I agree a non-hack and more-or-less code free solution should be found. Here are my thoughts:
The view is responsible because it sets IsDefault to true and allows for this 'problem'
The ViewModel should not be responsible in any way to fix this it might introduce dependencies from VM to V and thus breaking the pattern.
Without adding (C#) code to the View all you can do is either change bindings (e.g. to UpdateSourceTrigger=PropertyChanged) or add code to a base class of the Button. In the base class of the button you might be able to shift focus to the button before executing the command. Still hack-ish but cleaner than adding code to the VM.
So at the moment the only 'nice' solutions I see require the view developers to stick to a rule; set the binding in a specific way or use a special button.
I would add a Click event handler for the default button. The button's event handler is executed prior the command will be called, so the data bindings can be updated by changing the focus in the event handler.
private void Button_Click(object sender, RoutedEventArgs e) {
((Control)sender).Focus();
}
However, I don't know if similar approach can be used with other shorcut keys.
Yes, I have quite some experience. WPF and Silverlight still have their pain areas. MVVM doesn't solve it all; it is not a magic bullet and the support in the frameworks is getting better but still lacks. E.g., I still find editing deep child collections a problem.
At the moment I handle these situations case by case because a lot depends on the way the individual view have to work. This is what I spend most of my time on because I generate a lot of plumbing using T4 so I have time left for these quirks.
The problem is that the TextBox's text has a default source trigger of LostFocus instead of PropertyChanged. IMHO this was a wrong default choice since it is quite unexpected and can cause all sorts of problems (such as the ones you describe).
The simplest solution would be to always explicitly use UpdateSourceTrigger=PropertyChanged (as the others suggested).
If this isn't feasible (for whatever reason), I would handle the Unloaded, Closing or Closed events and manually update the binding (as shown by Rick).
Unfortunately it seems that certain scenarios are still a bit problematic with a TextBox, so some workarounds are necessary. For example, see my question. You might want to open a Connect bug (or two) with your specific problems.
EDIT:
Pressing Ctrl+S with focus on the TextBox, I would say the behavior is correct. After all, you are executing a command. This has nothing to do with the current (keyboard) focus. The command may even depend on the focused element! You are not clicking on a button or similar, which would cause the focus to change (however, depending on the button, it may fire the same command as before).
So if you want to only update the bound Text when you lose focus from the TextBox, but at the same time you want to fire a command with the newest contents of TextBox (i.e. the changes without it having lost focus), this does not match up. So either you have to change your binding to PropertyChanged, or manually update the binding.
EDIT #2:
As for your two cases why you cannot always use PropertyChanged:
What precisely are you doing with StringFormat? In all my UI work so far I use StringFormat to reformat data I am getting from the ViewModel. However, I am not sure how using StringFormat with data which is then again edited by the user should work. I am guessing you want to format the text for display, and then "unformat" the text the user enters for further processing in your ViewModel. From your description, it seems it isn't "unformatted" correctly all the time.
Open a Connect bug where it isn't working as it should.
Write your own ValueConverter which you use in the binding.
Have a separate property with the last "valid" value and use that value in your ViewModel; only update it once you get another "valid" value from the property you use in databinding.
If you have a long-running property setter (ex. a "validate" step), I would do the long-running part in a separate method (getters and setters should normally be relatively "fast"). Then run that method in a worker thread/threadpool/BackgroundWorker (make it abortable so you can restart it with a new value once the user enters more data) or similar.
What you think about proxy command and KeyBinding to ENTER key?
EDITED:
There we have one utility command (like converter), which requires knowledge about concrete view. This command can be reused for any dialog with same bug. And you add this functionality/hack only in view where this bug exists, and VM will be clear.
VM creates to adapt business to view and must provide some specific functionality like data conversion, UI commands, additional/helper fields, notifications and hacks/workarounds. And if we have leaks between levels in MVVM we have problems with: high connectivity, code reuse, unit testing for VM, pain code.
Usage in xaml (no IsDefault on Button):
<Window.Resources>
<model:ButtonProxyCommand x:Key="proxyCommand"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding Source={StaticResource proxyCommand}, Path=Instance}"
CommandParameter="{Binding ElementName=_okBtn}"/>
</Window.InputBindings>
<StackPanel>
<TextBox>
<TextBox.Text>
<Binding Path="Text"></Binding>
</TextBox.Text>
</TextBox>
<Button Name="_okBtn" Command="{Binding Command}">Ok</Button>
</StackPanel>
There used special proxy command, which receive element (CommandParameter) to move focus and execute. But this class requires ButtonBase for CommandParameter:
public class ButtonProxyCommand : ICommand
{
public bool CanExecute(object parameter)
{
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return false;
return btn.Command.CanExecute(btn.CommandParameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter == null)
return;
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return;
Action a = () => btn.Focus();
var op = Dispatcher.CurrentDispatcher.BeginInvoke(a);
op.Wait();
btn.Command.Execute(btn.CommandParameter);
}
private static ButtonProxyCommand _instance = null;
public static ButtonProxyCommand Instance
{
get
{
if (_instance == null)
_instance = new ButtonProxyCommand();
return _instance;
}
}
}
This is just idea, not complete solution.

Categories

Resources