Windows Phone Setting Visibility after an animation has executed - silverlight

I have a View and View Model. The View Model has a bool on there (Foo.CanSelect) that on change will set off a data trigger in the View.
This applies some funky fade out animation, and sets the visibility to collapsed in the final key frame. This is all well and good for the current session, but when I bring the app out from a tombstoning event (de-serialize it) the view is Visible again.
Foo.CanSelect is the correct value but it isn’t bound to the Visibility, because if it was, when the property is changed the visible state is immediately collapsed.
Am I going about this the wrong way?

You could persist the value of Foo.CanSelect when the application is tombstoned, and then read it on restore and update the visibility of the element accordingly. It may be easier to use visual states instead and simply store the required state on tombstoning and restore that state on resume.

Related

VisualStateManager in Silverlight: setting an initial state

I often find the need to include "away" and "present" type visual states that are used to animate the control being away or visible depending on some other condition.
The "away" state is then usually the one that should be the initial state. To my understanding, there is no way to define an initial state in SL but "base", which isn't really a state at all but denotes how the look is with the state manager being not yet active (no state storyboards are running to change the look of the control).
Of course you can design "base" to look like "away", but that means the default look in Expression Blend is invisible (you can't "pin" a state permanently either).
To change the initial state I tried
setting the state in the ctor of the control, which does nothing and
setting the state in a dispatched call from the ctor or the Loaded event, which both show the wrong state for a split-second.
So the problem appears to be that whatever the visual state manager does, it doesn't do it right away but needs a noticeable split-second to change the appearance.
(Setting the property directly for bootstrap is another option of course, but only works for UserControls: In templated Controls, I would have to introduce another depprop to template-bind the control template against, which is where I believe overkill starts.)
I suppose I covered it all and I just have to live with an invisible base state?
I use SL4.
I encountered a similar issue when developing a UserControl for WPF in Expression Blend (Note: if you're developing a custom control instead, see my next section). In that UserControl, I had a child element that I wanted to fade in and grow into existence as an overlay. Like your situation, it made sense in my workflow to first design the overlay element at its "fully grown and visible" state and then shrink it down and set its opacity for a "Hidden" state. In doing this, the overlay is visible in the Base state, but I needed the UserControl's initial state to be the Hidden state. At this point I had three main relevant states: Base, "Hidden" and "Visible" (these last two are a part of a State group).
Here's how I solved the initial-state issue. First I applied a GoToStateAction to the root element (to the UserControl) that is triggered by the Loaded event. It tells the UserControl to go right to the "Hidden" state:
<i:Interaction.Triggers>
<i:EventTrigger>
<ei:GoToStateAction TargetObject="{Binding ElementName=userControl}" StateName="Hidden"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Second, I made appropriate transition settings in the State Group for the overlay. There are probably several ways of doing this, but here's how I did it. First I set the "Default transition" to a pleasing setting, say .4 seconds. Next, I set the transition time from any element (the star icon in Blend) to this "Hidden" state to be 0 seconds (this allows the above-mentioned GoToStateAction to set the "initial" state without the user knowing any different). Then, I set the transition from the "Visible" state to the "Hidden" state to be an appropriate setting (say .4 seconds). Basically this covered all the bases for the transitions. The key was making sure that the "transition" from "any element" to the "Hidden" state was immediate, and then overriding that immediate transition in the case of going from the overlay's "Visible" to "Hidden" states.
Setting an Initial VisualState of a Custom Control
If you're developing a custom control (rather than a UserControl) and are thus defining your VisualStateManager in the control template, the above method (initiating the VisualState change based on the Loaded event) will probably not work. This is because the visual tree of your control (defined in a Style file) gets applied to your control right before the OnApplyTemplate() override gets called, which is usually after the first Loaded event has fired. So if you try to initiate a VisualState change in response to the Loaded event for a custom control, most likely nothing will happen. Instead, you will need to initiate the state change in your OnApplyTemplate() override code:
public class MyCustomControl : ContentControl
{
// ... other code ....
public MyCustomControl()
{
// avoid designer errors
if (DesignerProperties.GetIsInDesignMode(this))
return;
Loaded += new RoutedEventHandlerMyCustomControl_Loaded);
}
// This probably won't be called until AFTER OnApplyTemplate() gets
// called, so don't expect for your control to even have a visual tree
// yet when your control is first being contructed at runtime.
private void MyCustomControl_Loaded(object sender, RoutedEventArgs e)
{
}
public override void OnApplyTemplate()
{
// Avoid Visual Studio 2010 designer exceptions
// (Visual Studio can't handle the VisualState change at design-time)
if (DesignerProperties.GetIsInDesignMode(this))
return;
base.OnApplyTemplate();
// Now we know that the template has been applied, we have a visual tree,
// so state changes will work
VisualStateManager.GoToState(this, "MyInitialState", false);
}
}

Databinding falls behind event notification - discussion

Found an interesting problem that I first found in WinForms, and found again in Silverlight, and more than likely WPF as well when it comes to databinding.
I have a tab control with several tabs. As users click across the tabs, each time should be valid before allowing the user to switch from the tab.
For example, user is in a text box which is updated. Binding of text boxes is not flushed until the control loses focus. Loss of focus occurs when the cursor is moved from the control, and focus is given to another control.
In this scenario, the user tabs into a control (let's use text box for this example), and updates the text box. At this point the databinding has not flushed the control, and hence the VM has not yet seen the change. The user then uses their mouse to click the next tab of the control.
At this point things get interesting. I used the PreviewSelectionChanged (Telerik RadTabControl), as I want to check things out before the jump to the next tab occurs, and it also gives me the ability to cancel the event.
However, when I look at the VM, in this event, it still does not have the updated data. I see the VM is clean, and go ahead and allow the jump to the next tab.
As soon as this event is over however, the databindings flush, and the VM gets updated. what now? The events are out of sync! When the mouse was used to click the next tab, the textbox should have lost focus, flushed it's bindings, before the Preview of the Tab click! It's to late to jump back and say oops we didn't catch that in time!
I think I found an interesting work around to this issue - but I'm not 100% sure it will work 100% of the time. I cancel the current event, but then I use the Dispatcher and create a delegate pointing to another method with the same signature as the current event. The Dispatcher will add this message to the message pump, which by this time will now (hopefully?) be behind the messages of the VM updating...
My two questions are:
1) I'm assuming that the textbox control either didn't flush when the mouse left the control, or the process that was fired was too slow and hence the preview message was on the pump before the databinding - either way I see this to be a major issue.
2) Is the workaround a good solution?
Ok, first to answer question 1:
Just because the mouse left the textbox area, doesn't mean that the textbox lost focus. It only loses focus once something else gets focus. For example, if you moved the mouse out of the textbox and click on some other control on your page (it can be anything from a scroll viewer to another textbox, etc.) then your textbox will lose focus.
Now, based on that, the events do not happen in the wrong order. What happens is; your click event on the other tab triggers both the textbox to lose focus (and the data binding to take place) and the move to the next frame, and based on that, you basically get a race condition where the moving to the next tab happens before the databinding takes place.
About question 2:
What you can do is, set the UpdateSourceTrigger to Explicit, you will however be forced to then have some kind of text_changed event and manually update the binding.
You can read more about that here. It might not be the most complete explanation but is a good place to start.
On the other hand, you can associate some events to the textbox and force the textbox to lose focus on those events (e.g. mouse out).
Just an idea: Why not do everything in the VM's PropertyChanged event?
protected override void OnThisViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
if(e.PropertyName == "WhateverProperty") {
//Do your magic here for whatever you want to set
}
}
Have your TabItems bound to a collection that will control is being disabled or not.
<sdk:TabControl>
<sdk:TabItem IsEnabled="{Binding SomeProperty, Converter={AmIDisabledOrWhatConverter}}" />
</sdk:TabControl>
That way, everything is triggered whenever a property is chaned in the vm. No more timing issues since everything is on the vm.
Just my two cents.
There's a design defect here, and you're trying to work around the defect instead of fixing it. You shouldn't have to figure out how to cancel the Click event on the tab. The tab shouldn't be processing Click events in the first place.
Generally speaking, if it's not legal for the user to click on a control, the control shouldn't be enabled. The tab should be disabled until the state of the view model is valid.
Your view model should be exposing a command for navigating to the next tab, and the tab should be bound to the command. The command's CanExecute method should only return true when the state of the view model on the current tab is valid.
This doesn't fix your other problem, which is that Silverlight doesn't support UpdateSourceTrigger="PropertyChanged" out of the box. But that's a solved problem (here is one example).
Note that if you implement commands to handle this wizard-like navigation in your application, you can, down the road, change the view to use something other than a tab control (e.g. to use navigation buttons like an actual wizard, or something like Telerik's PanelBar) without having to screw around with event handlers.
Change your bindings to include UpdateSourceTrigger="PropertyChanged".
This will ensure that your data sources are updated on every key stroke, not just LostFocus.
MyOwnTextBox()
{
this.TextChanged += (s, e) => UpdateText();
}
private void UpdateText()
{
BindingExpression be = GetBindingExpression(TextProperty);
if (be != null && be.ParentBinding.Mode == BindingModes.TwoWay)
{
be.UpdateSource();
}
}
I am using this class it updates my binding on the fly, however there is issue with empty string and null values, if your destination property is nullable string then this will update empty string in your destination property. You can get workaround by using some sort of string converter which will use null instead of empty string in case of nullable strings.

Silverlight 4: How to trigger an Animation when TextBlock’s Text is changed during a DataBinding?

I have a user control which datacontext is set to a view model. The user control contains some textblocks and textboxes which are bind to view model's properties. I would like to do a small animation when a view model's property's value changes but I can't seem to find a way to do this in Silverlight.
In WPF we can use properties like NotifyOnTargetUpdated and Binding.TargetUpdated to capture the change but they seem to be missing from Silverlight. In my case the animation isn't based on the property's value, meaning I don't want to start the animation when some property's value is for example 5. Instead the animation should start every time the property's value is changed (to highlight the changed content for the user).
Here's the answer on how to do this in WPF: Animate WPF Text when binding updates, how?
But how can I do the same with Silverlight 4?
you should be able to get this done using the new trigger stuff included in Expression Blend 4. There's a whole bunch of ne behaviors/triggers etc. that let you react to changes in the ViewModel, for instance.
From the Expression Blend feature page:
New Behaviors
Expression Blend includes the new TranslateZoomRotateBehavior multi-touch Behavior, and a PanningItems control that you can use to scroll between items by using touch. Expression Blend also has a new trigger that responds to a frame change or the pausing or completion of a SketchFlow animation, called the SketchFlowAnimationTrigger. Expression Blend has new sets of Behaviors for dragging items between list boxes, for modifying menu navigation, and for preserving screen states, such as SetDataStoreValueAction and DataStoreChangedTrigger.
An exciting enhancement has been made to the FluidMoveBehavior: if you apply it to different views of the same data item, when the item moves from one view to another (for example, from a list view to a details view), it fluidly animates the transition between the two views.
New Behaviors for use with applications that use the Model-View-ViewModel pattern include the following: CallMethodAction, InvokeCommandAction, and DataStateBehavior. You can use these Behaviors to invoke behavior on your ViewModels, or to respond to changes to their properties.
Conditional Behaviors and the data store
You can now build conditional logic into your prototypes and production applications without the need to write code. In fact any action can be associated with a set of conditions that must be met in order to execute the action. The new data store feature enables application variables, such as the current user's role, for example, can be read from and written to so that, effectively, different parts of your application can communicate via this shared state.
New behavior components introduced as part of this feature include the conditions editor that appears in the Properties panel for every action, a SetDataStoreValueAction action that allows you to manipulate values in your data store, and a DataStoreChangedTrigger trigger that fires whenever a chosen property inside the data store is changed.
http://www.microsoft.com/expression/products/Blend_Features.aspx
Cheers, Alex

Distinguish UI change to variable from code-behind change

Like the title says I am trying to architecture into my application a way to distinguish the source of a variable change, either from UI or code-behind.
My problem is that I need to trigger some action after a property changed its value, but I only need to do this when the change comes from the UI because otherwise I don-t want to perform that action. I am having some trouble because, for example when a checkbox(two way binding), changes state, my binded property gets updated and then I use the checked and uncheked events to trigger that action.The problem is that when I change the property in codebehind it also triggers those events and I do not want that. Right now, i am using a flag that enables, or not, the actions at the event handlers but I do not feel that this is a good idea.
Any sugestions or ideas?
I am considering using only one-way binding and control everything my self, using commands.
It looks like you have some confusion between your model and your controller. There shouldn't be any cases where it matters if the a change to the model comes from the user or not. If you want to have something like a confirm message it the user makes the change, then don't bind the view control directly to the model, but have the controller respond to the event.
That way, if the control is changed to be the same as the model, then the change is internal, and no confirm is required, but if the control is changed by the user, then the control state differs from the model, and a confirm can be shown.

Silverlight: Is there an event that fires on a FrameworkElement before it is rendered?

In our Silverlight 2 project we have created an attached property to perform on-the-fly translation to text properties of various user controls. To achieve this, we hook the Loaded event of the FrameworkElement when the property is set. When the event fires, we take the existing text property value and perform some simple string substitutions on it, before replacing the property value with the translated text. However, this results in the control being rendered with the untranslated text, then the text is quickly replaced with the translated version.
Is there an alternate event we can hook that would fire before the control is rendered?
I've changed my code so that it now performs the translation as soon as the setter for the attached property is called. There's no need to wait for the FrameworkElement to have finished loading, as I can change the Text property long before the element is rendered.
My initial thoughts on using the Loaded event were to reduce the startup time of the application by only translating the controls that were visible on the screen. As it turns out, I'm duplicating some of the work performed by the runtime, as the runtime won't call the property setter until it needs to anyway.
I'm not totally sure about this, but can you use the LayoutUpdated event. It will fire when the control is resized and such (you could take measures to ensure your code only executes once.)
I know it doesn't seem like the "right" event for this but unfortunately Silverlight kinda leaves you standing there holding it when it comes to events.

Resources