How to reset a textbox in WPF in a button handler before doing something else? - wpf

I have a simple WPF button and a textbox in my WPF application(not using MVC or binding at all). I like to be able to do the following upon clicking the button:
1) Clear the textbox
2) Create the result
3) assign the result to the textbox
I used Textbox.Clear, TextBox.Text= String.Empty, delegates and a dispatcher approach like
private void button_Click(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(new Action (ClearReportTxtBox), DispatcherPriority.Send);
System.Threading.Thread.Sleep(5000);
runTest();
}
private void ClearReportTxtBox()
{
Report_textBox.Text = string.Empty;
}
None of them working correctly for me. The dispatcher method is somehow working but not as I wish. It seems that the Clear task will be queued and when the all actions in the button click handler are finished, it will come into play and delete the textbox, but this causes that the generated report and already assigned to the textbox (created by runtest in the code above) will be deleted as well. Hence it is too late delete action and eliminate the whole result.
Currently it seems to me that by clicking on the button the UIthread blocks and takes the control. The Dispatcher will queue the delete action as next action after finishing the button click.
Is it possible to force delete at the beginning and then do the rest? What I like to reach is that I pause the button activity and do delete at first action and then continue with the rest of actions in the button handler.
Am I doing something wrong?
Thank you for your help in advance.

The "Dispatcher.BeginInvoke" is kinda weird for what you want to do
All the UI update has to be done on main thread. Since the "Button_click" event is executing on main thread, the delegate you push into Dispatcher can only be executed AFTER the button_click handle is completed. That's why the execution sequence becomes
1. The GUI freeze because Thread.Sleep
2. RunTest
3. ClearReportTextBox
Guess you can try sth like the following instead.
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ClearReportTxtBox();
Task.Factory.StartNew(RunTest);
}
private void ClearReportTxtBox()
{
MyTextBox.Text = string.Empty;
}
private void RunTest()
{
System.Threading.Thread.Sleep(5000);
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.Invoke(priority, ()=> MyTextBox.Text = "123");
}
else
{
MyTextBox.Text = "123";
}
}

Related

Show() window wait for a button to be pressed and mainWindow active (WPF)

InteractiveWindow contain YES and NO button. The current solution is displaying the InteractiveWindow on MainWIndow without having access to MainWindow. InteractiveWindow is also a Window (not a UserControl). How is possible to transform my dialog window in order to have my InteractiveWindow on top of my MainWindow and be able to access MainWindow until one of the two buttons are pressed?
internal async Task<bool> Test()
{
// some code
var test= new InteractiveWindow();
test.Owner = this;
test.ShowDialog();
// some other code
return true;
}
Old code for one of the button:
private void YES_Click(object sender, RoutedEventArgs e)
{
GetWindow(this).DialogResult = true;
this.Close();
}
You should use
test.Show()
instead of test.ShowDialog(), because ShowDialog() is blocking. Even though your method is async, it executes synchronously until it needs to await an incomplete Task, so your Test method is blocking because there is no such await statement. I suggest you remove this async modifier if you don't use it.

Closing a Windows Form, using the Close Window button, when a Validation Message is showing

I have a Windows form that has a validation event on a textBox so that if the value of that TextBox is a value that already exists it triggers a validation error.
private void txtUsername_Validating(object sender, CancelEventArgs e)
{
var alreadyExists = _logic.UserIdExists(txtUsername.Text.Trim());
if(alreadyExists)
{
errorProvider1.SetError(txtUsername, "This Userid already exists, please choose an alternative");
e.Cancel = true;
}
}
private void txtUsername_Validated(object sender, EventArgs e)
{
errorProvider1.SetError(txtUsername, "");
}
this.txtUsername.Validating += new System.ComponentModel.CancelEventHandler(this.txtUsername_Validating);
this.txtUsername.Validated += new System.EventHandler(this.txtUsername_Validated);
This results in an error image appearing next to that textBox along with a tooltip error message.
If I try and close the application, using the Close button at the top of the window, at this time I cannot as the above Event keeps firing even when I try and close the window (due to me taking focus away from the Text box).
Is there a way of closing the window, without resorting to creating an additional Close button on the form?
Based on your description, you want to maintain the default auto-validation behavior yet allow the Form to be closed using the title bar close button. I have observed that the Form.Closing event is raised in such a circumstance, however its argument Cancel property is preset to true. A simple solution is to handle this event and set e.Cancel = false. Implement any logic in the handler that you deem necessary.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing) e.Cancel = false;
}

Winform Timer isn't running if statement when criteria is met

Hi I've come across either a weird bug or I'm not understanding something.
To cut a long story short I've had everything I'm wanting to work on my form working fine, I then decided to turn the form into an options menu so it was no longer the first form that appears when the application is launched and is shown after I click a button on a different form with the code
private void ShowOptionsButton_Click(object sender, EventArgs e)
{
formHomePage.Show();
}
And for some reason a timer if statement is no longer working:
private void StartSubCheckT_Tick(object sender, EventArgs e)
{
if (subliminalMessages.Count > 0)
{
MessageBox.Show("list greater than 0 if");
StartSubB.Enabled = true;
}
there are other if statements below but are irrelevant and the point of this is to make a button usable once a list is greater than 0. I've created another test button to display the value and it shows that the sublminalMessages list is greater than 0
private void testbutton3_Click(object sender, EventArgs e)
{
MessageBox.Show(subliminalMessages.Count.ToString());
}
Which outputs at 1 which it should be from some other code that adds a value in at the beginning. But for some reason even with the subliminalmessages.count being greater than 0 the if statement is no longer being called ever since I'm making the form appear being called from another form from the button code above.
The subliminalMessages list is being populated and created on the same form
public List<string> subliminalMessages = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(Settings.Default["Subliminal1"].ToString()))
{
subliminalMessages.Add(Settings.Default["Subliminal1"].ToString());
MessageBox.Show("If worked");
}
}
There is a value in the Setting.Default that is being added
The button and timer are on the same form and the timer in question is enabled.
Does anyone know why?
Thanks
I'll have a stab at giving you an answer. But it's a little swerve from what you're doing now.
From what I understand of your code you are using the timer to enable/disable the StartSubB button. Or maybe just enable it.
Instead of relying on the timer which appears to not work why not use a BindingList<string>. This has an event called ListChanged which you can handle and then enable/disable your button.
Here's a test form I created:
public partial class Form1 : Form
{
BindingList<string> items;
public Form1()
{
InitializeComponent();
button3.Enabled = false;
items = new BindingList<string>();
items.ListChanged += Items_ListChanged;
}
private void Items_ListChanged(object sender, ListChangedEventArgs e)
{
button3.Enabled = items.Count > 0;
}
private void btnAdd_Click(object sender, EventArgs e)
{
items.Add("a");
}
private void btnRemove_Click(object sender, EventArgs e)
{
if (items.Count > 0)
items.RemoveAt(items.Count - 1);
}
}
I have a BindingList<string> called items. This is analagous with your subliminalmessages list but it's a BindingList. This type of list has a ListChanged event that fires when items are added or removed from the list. In the constructor we new up the items list and subscribe to the ListChanged event.
When the Items_ListChanged event fires button3 is enabled or disabled based on whether items.Count > 0 or not.
In btnAdd_Click we just add an item to the list.
In btnRemove_Click we check that there are some items then remove the last one.
If you were to run this you'd see that when we click the Add button, button3 gets enabled. If we click the Remove button we'll see button3 get disabled.
The only limitation of BindingList is that you can't add a range by passing in another List.
If you implement this and your button still doesn't activate then you'll probably need to post some more code. Strip out all the irrelevant stuff and put it in a new project that demonstrates the failure of the condition and either copy the code here or provide a link to download the project. The only reason the if statement should fail is if the list is actually empty.

Windows.Activated in wpf fires multiple times

Hi I have a code to be written while window(WPF window) activate like clicking on the window or using alt/tab. The window is the child of a main form (windows app). I have used ToolWindow as the windowstyle.
It has a xamdatagrid which has to updated on activation
Problem is it fires multiple times. It should be fired once. I don not want my code to run multiple times
How to make it work. please help
From the Window.Activated Event page on MSDN:
Occurs when a window becomes the foreground window.
The Window.Activated Event is supposed to be called multiple times, so perhaps it is not the best event for you to handle. Alternatively, you could add a bool isFirstTime variable and use it to restrict your code to only being called once. Take this example:
private bool isFirstTime = true;
...
private void WindowActivated(object sender, EventArgs e)
{
if (isFirstTime)
{
isFirstTime = false;
// do something here just once
}
}
However, as (from the linked page)...
A window is activated (becomes the foreground window) when:
• The window is first opened.
• A user switches to a window by selecting it with the mouse, pressing ALT+TAB, or from Task Manager.
• A user clicks the window's taskbar button.
... you may find that this will not work for you.
I got it done.
I was using the below code
private void OnAttributeHistoryWindowActivated(object sender, EventArgs e)
{
var win = ((RoutedEventArgs)(e)).Source as AttributeHistoryWindow;
//My Code
}
The first line of code was firing back the Activated event. And it never goes to the next line of my code.
Now I used below code and it works.
private void OnAttributeHistoryWindowActivated(object sender, EventArgs e)
{
var win = sender as AttributeHistoryWindow;
//My Code
}
Now it fires once.

Update UI from ViewModel class (MVVM pattern) in WPF

I'm using the MVVM pattern in my first WPF app and have a problem with something quite basic I assume.
When the user hits the "save" button on my view, a command gets executed that calls the private void Save() in my ViewModel.
The problem is that the code in "Save()" takes some time to execute, so I'd like to hide the "Save" button in the UI view before executing the large chunk of code.
The problem is that the view doesn't update untill all code is executed in the viewmodel.
How can I force the view to redraw and process the PropertyChanged events before executing the Save() code?
Additionally, I would like a reuseable way, so that I can easily do the same thing in other pages as well.. Anyone else made something like this already? A "Loading..." message?
If it takes a long time, consider using a separate thread, for example by using a BackgroundWorker, so that the UI thread can stay responsive (i.e. update the UI) while the operation is performed.
In your Save method, you would
change the UI (i.e. modify some INotifyPropertyChanged or DependencyProperty IsBusySaving boolean which is bound to your UI, hides the Save button and maybe shows some progress bar with IsIndeterminate = True) and
start a BackgroundWorker.
In the DoWork event handler of your BackgroundWorker, you do the lengthy saving operation.
In the RunWorkerCompleted event handler, which is executed in the UI thread, you set IsBusySaving to false and maybe change other stuff in the UI to show that you are finished.
Code example (untested):
BackgroundWorker bwSave;
DependencyProperty IsBusySavingProperty = ...;
private MyViewModel() {
bwSave = new BackgroundWorker();
bwSave.DoWork += (sender, args) => {
// do your lengthy save stuff here -- this happens in a separate thread
}
bwSave.RunWorkerCompleted += (sender, args) => {
IsBusySaving = false;
if (args.Error != null) // if an exception occurred during DoWork,
MessageBox.Show(args.Error.ToString()); // do your error handling here
}
}
private void Save() {
if (IsBusySaving) {
throw new Exception("Save in progress -- this should be prevented by the UI");
}
IsBusySaving = true;
bwSave.RunWorkerAsync();
}
You're using MVVM pattern, so your Save Button's Command is set to an instance of the RoutedCommand object which is added to the Window's CommandBindings collection either declaratively or imperatively.
Assuming that you do it declaratively. Something like
<Window.CommandBindings>
<CommandBinding
Command="{x:Static namespace:ClassName.StaticRoutedCommandObj}"
CanExecute="Save_CanExecute"
Executed="Save"
/>
</Window.CommandBindings>
For the handler of Executed routed event, your Save() method, on entry, you set a variable to false, on return you set it back to true. Something like.
void Save(object sender, ExecutedRoutedEventArgs e)
{
_canExecute = false;
// do work
_canExecute = true;
}
For the handler of the CanExecute routed event, the Save_CanExecute() method, you use the variable as one of the condition.
void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _canExecute && _others;
}
I hope I am clear. :)
You could always do something like this:
public class SaveDemo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set
{
if (_canSave != value)
{
_canSave = value;
OnChange("CanSave");
}
}
}
public void Save()
{
_canSave = false;
// Do the lengthy operation
_canSave = true;
}
private void OnChange(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
}
Then you could bind the IsEnabled property of the button to the CanSave property, and it will automatically be enabled/disabled. An alternative method, and one I would go with would be to use the Command CanExecute to sort this, but the idea is similar enough for you to work with.
You can accomplish this by the following code..
Thread workerThread = null;
void Save(object sender, ExecutedRoutedEventArgs e)
{
workerThread = new Thread(new ThreadStart(doWork));
SaveButton.isEnable = false;
workerThread.start();
}
do all your lengthy process in dowork() method
in some other method...
workerThread.join();
SaveButtton.isEnable = true;
This will cause to run save lengthy process in another thread and will not block your UI, if you want to show an animation while user click on save button then show some progress bar like iPhone etc... give me feedback i'll try to help you even more.
Late answer, but I figured it'd be good to input a bit as well.
Instead of creating your own new thread, it would probably be better to leave it up to the threadpool to run the save. It doesn't force it to run instantly like creating your own thread, but it does allow you to save threading resources.
The way to do that is:
ThreadPool.QueueUserWorkItem(Save);
The problem with using this approach, as well, is that you're required to have your "Save()" method take in an object that will act as a state. I was having a similar problem to yours and decided to go this route because the place that I'm working is very Resource-Needy.

Resources