Button state not updating - wpf

I have a small WPF application. This application has a button that when clicked, should have its text changed and be disabled. The code for my button event handler is as follows:
/// <summary>
/// 'Read' button clicked
/// </summary>
/// <param name="sender"></param>
/// <param name="routedEventArgs"></param>
private void ReadVersionNumber(object sender, RoutedEventArgs routedEventArgs)
{
Read.Content = "Reading....";
Read.IsEnabled = false;
SerialPort p = new SerialPort();
string response = "Could not read version";
try
{
// Do some stuff
}
catch (Exception)
{
response = "There was an error while reading the version number";
}
finally
{
Read.IsEnabled = true;
Read.Content = "Read";
if(p.IsOpen)
{
p.Close();
}
}
Version.Text = response;
}
The problem is my button text never changes, and it doesnt become disabled. Ive tried calling UpdateLayout after setting the button properties, but it still doesnt change. The button locks up while it runs through the method, then only updates its layout right at the end of the method. What do i need to do to update the layout?

In the finally (which ALWAYS executes) you set:
Read.IsEnabled = true;
Read.Content = "Read";
Further: if you change a property of a button multiple times in the same function that runs on the UI thread you will not see any changes because the updates will be too fast because the UI thread updates the UI AFTER the method has executed.
If you want to see the changes you could use a backgroundworker and in the ProgressChanged handler update the UI.

It's because you're doing it all on the same thread, so therefore control never returns to WPF so that it can update the state. You need to either split your work onto multiple threads (see BackgroundWorker component) or set the button status using separate messages (see Dispatcher class).

Related

How do I group together radio buttons that are in a custom control?

I have a custom control (not component) based off a radio button that has a fancy label that will glow green when the label is selected, and black when it is not to give better visual feedback and because why not.
To confirm my fears I dropped a bunch on a form and ran it and, sure enough, they weren't considered "grouped together" in that I could activate all of them and none of the others deactivated.
How am I to be able to make it so that each of these controls will be part of a "Special Radio Button Group" and behave as a normal radio button would?
Okay so I found "A" solution. I don't know if it's an IDEAL solution but it works.
I had had the object function off of looking for the "CheckedChanged" event. The problem is that this fires whether it was done manually or programatically.
Solution? Use the "Clicked" event. But not just on the button, on the entire thing.
So what we get is this:
namespace Pieces{
public partial class ucGraphicLabelRadioButton : UserControl{
private event EventHandler _CheckedChanged;
/// <summary>
/// Gets or sets the controls text.
/// </summary>
public override string Text{
get{return this.lblTitle.Text;}
set{lblTitle.Text = value;}
}
/// <summary>
/// Gets or sets the checked state of the button.
/// </summary>
public bool Checked{
get{return this.rbtnButton.Checked;}
set{this.rbtnButton.Checked = value;}
}
public event EventHandler CheckedChanged{
add{this._CheckedChanged += value;}
remove{this._CheckedChanged -= value;}
}
public ucGraphicLabelRadioButton(){
InitializeComponent();
}
//This is where the fancy stuff happens...
private void ToggleCheck(object sender, EventArgs e){
this.lblTitle.GlowColor = Color.Green;
bool FoundOtherChecked = false;
this.Parent.Controls.OfType<ucGraphicLabelRadioButton>().Where(x => x.Checked && x != this).ToList().ForEach(x => {
x.Checked = false;
x.lblTitle.GlowColor = Color.Black;
FoundOtherChecked = true;
});
if ((FoundOtherChecked && !this.Checked) || !this.Checked){
this.Checked = !this.Checked;
this.lblTitle.GlowColor = this.rbtnButton.Checked ? Color.Green : Color.Black;
}
if (this._CheckedChanged != null)
this._CheckedChanged(this, new EventArgs());
}
}
}
The "ToggleCheck" event is tied to the RadioButton objects "Click" event as well as the labels "Click" event and the entire custom controls "Click" event so ideally clicking anywhere on the label will fire the event off.
When the object is toggled it runs a linq search for any object that is like itself in the parent control and grabs it ONLY if it's checked. Then, it unchecks it and sets the glow state to black.
The control also has an event that forwards any sort of event you want to tie to the radio buttons "CheckedChanged" event and then fires it.
I know that doing the whole
.ToList().ForEach(x => ...)
is total overkill since the search would only ever return a single object (or none in the case it was turning itself off) but it does work. Is there a better way to do this?
EDIT: I had to make some changes since evidently when you click directly on the radio button, it becomes checked first THEN the clicked event fires. Also, to make sure they behave more like radio buttons and less like check boxes with radio-button like behavior, I.E.: The user can't click them off now. It checks to see if one was toggled on before toggling itself off.

MVVM multiple Dialogs headache

I am using caliburn micro. My problem is how to manage dialogs.
The biggest problem is that because when not using window your code doesnt stop and wait.
So i did something like this.
public void ShowDialog(IScreen dialogModel, Action<object> callback = null)
{
ActivateItem(dialogModel);
if (callback != null)
dialogModel.Deactivated += delegate { callback(dialogModel); };
}
This has lots of problem.For example in case i want to show dialog A and then at callback i want to show dialog B under certain cases there comes a problem.I have to write an extra function for DoSomething in order not to duplicate.And i loose all the other local variables..The problem is bigger when more levels are required..
showDialog(A, (cb)=>{
if(...) {
showDialog(B,(cb2)=>{
DoSomething();
});
}
else{
DoSomething();
}
});
Also because i wanted to show one dialog at a time i extended Collection.OneActive . But this had problem too. In deactivate event callback i couldnt close all if i wanted to! Because it keeps in memory the next reference after Deactivated is triggered and even if you clear it it comes again..
How about using a class to track state information as you move between dialogs, rather than nesting closures as shown in your original example?
I think you're on the right track, but it seems like you have two problems:
The amount of nesting that you're doing is not good for code clarity.
You need a better way to capture local variables and state information between dialogs.
To solve the first problem, I'd recommend breaking apart your logic into different methods. Every time a dialog is deactivated, you could have a method to handle the logic that should be executed afterward.
To solve the second problem, you might try creating a class that is responsible for storing the information that you want to pass between dialogs. An instance of this class could be passed in as an argument into each method that is to be executed upon dialog deactivation.
Here's how you could accomplish this:
Conductor Class
public class DialogTestsViewModel : Conductor<object>.Collection.OneActive
{
/// <summary>
/// Shows a dialog and executes its callback if necessary.
/// </summary>
/// <param name="dialogModel">The dialog view model to be shown.</param>
/// <param name="callback">The callback to be executed when dialog is closed.</param>
public void ShowDialog(IScreen dialogModel, Action callback = null)
{
// Show the dialog.
ActivateItem(dialogModel);
// If there is a callback, call it when dialog is closed / deactivated.
if (callback == null) return;
dialogModel.Deactivated += (sender, args) => callback();
}
/// <summary>
/// This method kicks off the dialog chain.
/// </summary>
public void ShowFirstDialog()
{
// Create a new context. This will hold state information
// as it is passed between dialogs.
var context = new TestDialogContext();
// Create the first dialog's view model.
var viewModel = new FirstDialogViewModel();
// Show the first dialog.
ShowDialog(viewModel, () => OnFirstDialogDeactivated(viewModel, context));
}
/// <summary>
/// Logic to be executed when the first dialog is closed.
/// </summary>
/// <param name="viewModel">The first dialog's view model.</param>
/// <param name="context">The state information.</param>
private void OnFirstDialogDeactivated(FirstDialogViewModel viewModel, TestDialogContext context)
{
// Check the view model here and store state information inside the context.
if (viewModel.SomethingIsChecked)
{
context.ShouldShowSecondDialog = true;
}
// Use information in the view model or the context to decide if we should show the next dialog.
// You could also make a decision about which dialog to show next here.
if (context.ShouldShowSecondDialog)
{
var secondDialog = new SecondDialogViewModel();
ShowDialog(secondDialog, () => OnSecondDialogDeactivated(context));
}
}
/// <summary>
/// Logic to be executed when the second dialog is closed.
/// </summary>
/// <param name="context">The state information.</param>
private void OnSecondDialogDeactivated(TestDialogContext context)
{
// Do more stuff.
}
}
Dialog Context Class
Here is where you would store the state information that needed to be passed between dialogs. I've only included one property here as an example, but you could put a lot of info here.
/// <summary>
/// State information to be passed between dialogs.
/// </summary>
public class TestDialogContext
{
public bool ShouldShowSecondDialog { get; set; }
}

Execute command after view is loaded WPF MVVM

I have a project based WPF and MVVM.
My project is based on a wizard containing a content control which shows my views (User Controls)
I want to execute a command after the view is loaded completely, I would like the user to see the view UI immediately after the command will be executed.
I tried using :
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding StartProgressCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
But the command is executed before I see the view UI and it's not what I'm looking for.
Does anyone have an idea how should I need to implement it?
You could use the Dispatcher for this and set the priority to ApplicationIdle so that it will on execute when everything has finished
Application.Current.Dispatcher.Invoke(
DispatcherPriority.ApplicationIdle,
new Action(() =>
{
StartProgressCommand.Invoke(args);
}));
more information on the dispatcher http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx
cheers.
ste.
That's because even though technically the view is loaded (i.e: all the components are ready in memory), your app is not idle yet, and thus the UI isn't refreshed yet.
Setting a command using interaction triggers on the Loaded event is already good, as there is no better event to attach to.
Now to really wait until the UI is shown, do this in your StartProgress() (I'm assuming here that this is the name of the method that StartProgressCommand point to):
public void StartProgress()
{
new DispatcherTimer(//It will not wait after the application is idle.
TimeSpan.Zero,
//It will wait until the application is idle
DispatcherPriority.ApplicationIdle,
//It will call this when the app is idle
dispatcherTimer_Tick,
//On the UI thread
Application.Current.Dispatcher);
}
private static void dispatcherTimer_Tick(object sender, EventArgs e)
{
//Now the UI is really shown, do your computations
}
another way to do it:
define this xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" and xmlns:mi="http://schemas.microsoft.com/expression/2010/interactions" on your usercontrol XAML and add Microsoft.Expression.Interactions assembly on your project. use CallMethodAction on your trigger, just as bellow:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<mi:CallMethodAction TargetObject="{Binding}" MethodName="StartProgressCommand"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Put the triger inside the root element of your usercontrol, e.g: grid. And change your StartProgressCommand, in your ViewModel class, from command to plain old regular Method, e.g:
public void StartProgressCommand()
{
/* put your program logic here*/
}
It'll run the method exactly one time every time your user control rendered.
We use a the timer solution - i too was very dubious about this but it does seem to work fine.
public static class DispatcherExtensions
{
private static Dictionary<string, DispatcherTimer> timers =
new Dictionary<string, DispatcherTimer>();
private static readonly object syncRoot = new object();
public static void DelayInvoke(this Dispatcher dispatcher, string namedInvocation,
Action action, TimeSpan delay,
DispatcherPriority priority = DispatcherPriority.Normal)
{
lock (syncRoot)
{
RemoveTimer(namedInvocation);
var timer = new DispatcherTimer(delay, priority, (s, e) =>
{
RemoveTimer(namedInvocation);
action();
}, dispatcher);
timer.Start();
timers.Add(namedInvocation, timer);
}
}
public static void CancelNamedInvocation(this Dispatcher dispatcher, string namedInvocation)
{
lock (syncRoot)
{
RemoveTimer(namedInvocation);
}
}
private static void RemoveTimer(string namedInvocation)
{
if (!timers.ContainsKey(namedInvocation)) return;
timers[namedInvocation].Stop();
timers.Remove(namedInvocation);
}
}
Then we invoke using
Dispatcher.CurrentDispatcher.DelayInvoke("InitSomething",()=> {
DoSomething();
},TimeSpan.FromSeconds(1));
You can check IsLoaded property for view, when view is in loaded form it returns false, when view is fully loaded, this property become true.
Thanks,
Rajnikant
Have you tried binding to the ContentRendered event? It will occur after the loaded event, yet I´m not sure whether or not this is a gurantee that the UI thread has finished painting the window then.
You can Write a "Thread.Sleep(10000)" in the first line of "CommandExecute" method. Use the same loaded trigger.
if you don't want to use Thread.Sleep then you can go for "DispatcherTimer". Start a timer in your command execute method and shift all your code to timer tick event.
set your timer interval to 2 seconds, so that user will sen the UI.
I came with this solution for this one.
I wanted to use a boolean property set as true at start of work and to false at the end to allow to notify user of background work.
Basically, it uses
a DispatcherTimer to launch a method after UI render according to this
An async method wich will execute the Action passed as a parameter
Call :
this.LaunchThisWhenUiLoaded(() => { /*Stuff to do after Ui loaded here*/ });
Method :
private DispatcherTimer dispatchTimer;
private Action ActionToExecuteWhenUiLoaded;
/// <summary>
/// Handy method to launch an Action after full UI rendering
/// </summary>
/// <param name="toExec"></param>
protected void LaunchThisWhenUiLoaded(Action toExec)
{
ActionToExecuteWhenUiLoaded = toExec;
// Call UiLoaded method when UI is loaded and rendered
dispatchTimer = new DispatcherTimer(TimeSpan.Zero, DispatcherPriority.ContextIdle, UiLoaded, Application.Current.Dispatcher);
}
/// <summary>
/// Method called after UI rendered
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected async void UiLoaded(object sender, EventArgs e)
{
this.IsBusy = true;
if (ActionToExecuteWhenUiLoaded != null)
await Task.Run(ActionToExecuteWhenUiLoaded);
dispatchTimer.Stop();
this.IsBusy = false;
}
Maybe not the clean but it works as expected.

Bound Button Not Enabling After Background Worker Process Completes

I have a background worker process that starts provisioning a new client for our system. Here is what the DoWork method looks like:
ProvisioningManager manager = new ProvisioningManager(false)
{
};
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.MaxSteps = manager.MaxProgress;
}));
manager.StatusUpdated += new ProvisioningManager.StatusUpdatedHandler(manager_StatusUpdated);
manager.TaskCompleted += new ProvisioningManager.TaskCompleteHandler(manager_TaskCompleted);
manager.ProvisionClient();
while (!manager.Completed)
{
System.Threading.Thread.Sleep(100 * 60);
}
Basically it creates the manager that handles talking to the different sub-systems which provision the client.
Now I have a status update event and completed event for the provisioning manager. When the TaskCompleted event fires I want to be able to set a property on my display object so that the finish button in the wizard is enabled:
void manager_TaskCompleted(object sender, ProvisioningManager.Task taskType)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
The XAML for the button looks like this:
<wizard:WizardPage Header="Provisioning Client..."
ShowBack="False"
AllowBack="False"
AllowFinish="{Binding Source={StaticResource ResourceKey=dataObject}, Path=ProvisioningComplete}"
Loaded="Provisioning_Loaded">
</wizard:WizardPage>
This isn't working. Even though I make sure to hit the dispatcher thread to set the property of the display object it doesn't actually change the button to enabled until I click on the window. Is this a bug in AvalonWizard or am I not on the correct thread to set an INotifyPropertyChanged? Is there a way to hack this; basically can I programmatically focus the window without the mouse click?
I tired placing that while loop in the DoWork method so that I could use the BackgroundWorker's completed method:
void provisioningWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
this.ProvisioningComplete = true;
}));
}
That doesn't work either. What gives?!
Update
Here is the requested static resource instantiation for the display object:
<Window.Resources>
<ObjectDataProvider x:Key="dataObject" ObjectType="{x:Type winDO:NewClientWizardDO}" />
</Window.Resources>
Update II
Here is the property and property change firer:
public bool ProvisioningComplete
{
get { return this._ProvisioningComplete; }
set
{
this._ProvisioningComplete = value;
this.NotifyPropertyChanged("ProvisioningComplete");
}
}
protected void NotifyPropertyChanged(params string[] propertyNames)
{
if (this.PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
sorry if I don't understand something, but is the ProvisioningComplete property marked as "volatile"? If not then this might be the problem.
So I couldn't find out exactly why I was having this issue. I tried setting focus to the window, the button, etc. I tried multiple ways of letting the view know the viewmodel had updated. Basically every suggestion I could find on the web didn't work. It almost seems like a bug.
A smarty on my team suggested faking a mouse click on the window. His idea was that since all it took to activate the button was a simple mouse click on the screen then faking one should have the same effect. I thought (and think) that this hack was ridiculous. I did try it out just to see if I could call it a "solution".
Well, it worked. We had this same problem in another one of our wizards (not AvalonWizard but a homegrown one). I think there has to be some underlying issue with the way the window redraws after a background thread updates objects that are bound to the UI.
Anyhow, the way I found to solve this issue is with the following hack-tastic code.
//import user32.dll and setup the use of the mouse_event method
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
/// <summary>
/// Watches for properties to change on the data object, mainly the ProvisioningComplete method
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DataObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ProvisioningComplete":
//if the provisioning is completed then we need to make the finish button selectable.
if (this.DataObject.ProvisioningComplete)
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
//give the window focus
this.Focus();
//update the layout
WizardPageProvisioningClient.UpdateLayout();
//fake mouse click 50 pixels into the window
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)(this.Left + 50), (uint)(this.Top + 50), 0, 0);
}));
}
break;
}
}
I've tested this when the window is not the active window and when the user leaves the window as selected. The focus method seems to take care of this issue when the window isn't active. Our QA team hasn't run a complete test against the UI so I can't say if there is any situations where it doesn't work, but it seems to be the best solution that I've come up with to date.
I'm open to any other suggestions if anyone out there has a better idea of what could be causing the button to not update.

WPF image visibility is not changing

I have the following method in my WPF project (.net 4):
private void MyMethod(){
imgMyImage.Visibility = Visibility.Visible;
DoWork();
imgMyImage.Visibility = Visibility.Collapsed;
}
The image is in a DockPanel, and I want it to appear while the "DoWork()" method is being executed, but it does not change state until after the "MyMethod()" method exits. Can someone explain how to make this work correctly?
Thank you for any help.
Your "DoWork" method is blocking the UI thread. Until it completes, nothing in the UI will change (and the UI will remain unresponsive).
A better option is to push the DoWork into a background thread. For example, using the new Task framework in .NET 4, you could write this as:
private void MyMethod()
{
imgMyImage.Visibility = Visibility.Visible;
// Create a background task for your work
var task = Task.Factory.StartNew( () => DoWork() );
// When it completes, have it hide (on the UI thread), imgMyImage element
task.ContinueWith( t => imgMyImage.Visibility = Visibility.Collapsed,
TaskScheduler.FromCurrentSynchronizationContext() );
}

Resources