How can I animate a property dynamically in a Silverlight 4 UserControl? - silverlight

I've run into a puzzling limitation in a Silverlight 4 UserControl.
What I'm trying to achieve is to have a panel, which slides out from a minimised state when a button is pressed, but the title bar of it should be draggable with which this maximised state can be resized.
What I've done for the sliding out is to animate the MaxHeight property of the parent Grid of this panel which works quite well even with no hardcoded Height for the panel, but I don't know how can I make this dynamic.
Trying to bind a variable from the code-behind to the 'To' parameter of the 'DoubleAnimation' didn't work, it just silently gets ignored.
As I'm creating UserControls to represent Views, the elements with x:Name properties won't get autogenerated.
I tried to work around this using the code below which mimics what happens in the autogenerated code (with the added bonus of only being done after the layout is actually loaded):
public DoubleAnimation PanelOpenMaxHeightDoubleAnimation;
private void LayoutRoot_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var LayoutRootreference = sender as Grid;
PanelOpenMaxHeightDoubleAnimation = ((DoubleAnimation)(LayoutRootreference.FindName("PanelOpenMaxHeightDoubleAnimation")));
PanelOpenMaxHeightDoubleAnimation.To = 383;
}
This however breaks when trying to set the value of To, as FindName returns null (I have x:Name manually set in XAML for this particular animation to "PanelOpenMaxHeightDoubleAnimation"). I have the sneaking suspicion FindName can't pick DoubleAnimations up from VisualStates, only actual layout children?
I did find the documentation about XAML Namescopes at http://msdn.microsoft.com/en-us/library/cc189026(v=VS.95).aspx#UserControls, but didn't really understand what my options are from this paragraph (other than being very limited):
For the case of a UserControl, there is no equivalent template part attribute convention for parts of the UserControl in the definition XAML, nor is there a template applied at all. Nevertheless, the namescopes between definition and usage remain disconnected, because the definition namescope is defined and then effectively sealed when you package your UserControl into an assembly for reuse. A best practice here is to define your UserControl such that any value that needs to be set to modify the definition XAML is also exposed as a public property of the UserControl.
What does it mean by the last sentence?
Wondering can I do next? Should I try to generate the entire state from code?

Well, managed to work it out so I'm sharing the solution.
Instead of trying to get a reference to the DoubleAnimation in Resources, I named the Grid in the layout I want to animate and get a reference to that using the code in the original question:
var SlidePanel = ((Grid)(LayoutRootreference.FindName("SlidePanel")));
This does return the element and using that it's possible to create a DoubleAnimation and a Storyboard from scratch purely in code. I just used this code example as a starting point: http://msdn.microsoft.com/en-us/library/cc189069(VS.95).aspx#procedural_code
Best part is, you can change the DoubleAnimation.To parameter even after setting everything up in the Storyboard, so now what I'm doing is just resetting that to my calculated value every time before calling Storyboard.Begin().
It's a bit fiddly to set all these up manually, but at least it works nicely once you do.

Related

Animate a Helix Toolkit control drawn by a usercontrol

I am trying to achieve something very similar to this:
WPF with Helix toolkit, animate with code-behind?
However, my scenarios is slightly different. I am trying to animate a tube (curve) defined by a path (TubePath) which undergoes deformation as a function of time (animation) defined by a mathematical function (calculated numerically).
My design/plan is to use a usercontrol (containing the Helix Tube control) to draw each frame of the tube defined by a path. I plan to make the TubePath property in my usercontrol which is an oservablecollection a dependency property. I plan to control the animation from my ViewModel (or View) by binding the CurrentPath property of the ViewModel to the usercobtrol TubePath property. I plan to drive my ViewModel from INotifyPropertyChanged. I will a have List of Paths (PathList) stored in advance of starting the animation in my Model. At each (so called) time or animation step, I will copy the corresponding path from the PathList to the CurrentPath in the VieModel. I am hoping that change in the the CurrentPath collection in the ViewModel will activate the binding which will force the user control to update the tube drawn by the usercontrol.
These are my questions:
(1) I have read that not all changes to an ObservableCollection fires propertychanged events. Will overwriting or refereshing the whole CurrentPath collection in the ViewModel update the TubePath in the UserControl which will force redrawing of the tune? Do I have to do anything special to achieve this kind of binding.
(2) I am new to WPF and animations with WPF. My research indicated that I could do the animation from my ViewModel in several ways: using the Dispatcher.Invoke() like in the example given above, or I could use the RenderingEventManager.AddListener() (and RenderingEventManager.RemoveListener() to stop animation) provided by the Helix Toolkit like it is done in example here:
https://github.com/helix-toolkit/helix-toolkit/tree/develop/Source/Examples/WPF/ExampleBrowser/Examples/Wind
or I could loop using DispatcherTimer() as done here:
Binding on DependencyProperty of custom User Control not updating on change
Any suggestions as to the best method as well as my concept of driving animation from the ViewModel would be greatly appreciated.

WPF schema - what defines a ListBox to have a ScrollViewer?

For years, I have felt I don't have a good understanding of WPF because I haven't found an authoritative reference on the possibilities. For example, I just found out that a ListBox has an attached ScrollViewer property.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Auto">
Other than reading a book or article that tells that, how would I know that ScrollViewer is a valid attached property? Is there a XAML schema document or something? How does Visual Studio Designer know?
ScrollViewer isn't an attached property; it's a class.
ScrollViewer.HorizontalScrollBarVisibility is an attached property. But it's not an attached property that ListBox "has"; it's an attached property that can be attached to any DependencyObject at all, including ListBox.
Here's what you see if you right click on ScrollViewer.SetHorizontalScrollBarVisibility and ScrollViewer.GetHorizontalScrollBarVisibility. A pair of static methods like this is required for an attached property. The first parameter is the thing you're attaching the property to. It doesn't have to be DependencyObject; it could be FrameworkElement, ListBox, ItemsControl, or anything else that can support dependency properties.
// Summary:
// Sets the value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property to a given element.
//
// Parameters:
// element:
// The element on which to set the property value.
//
// horizontalScrollBarVisibility:
// The property value to set.
public static void SetHorizontalScrollBarVisibility(DependencyObject element, ScrollBarVisibility horizontalScrollBarVisibility);
//
// Summary:
// Gets the value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property from a given element.
//
// Parameters:
// element:
// The element from which the property value is read.
//
// Returns:
// The value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property.
public static ScrollBarVisibility GetHorizontalScrollBarVisibility(DependencyObject element);
The ListBox itself quite likely has no clue what ScrollViewer.HorizontalScrollBarVisibility means, or even that it exists. But in the ListBox's template, there's probably a ScrollViewer, which will probably have a binding a lot like this:
<ScrollViewer
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
Put that attached property on any control that might have a ScrollViewer in its template, and if the template was written correctly, the ScrollViewer will use that value.
This is really, really nice because ListBox doesn't have to think about its own scrolling behavior. That's all delegated. Building controls by composition is extremely powerful.
The downside is that the whole thing is just a vast box of undifferentiated barf and it's real hard to make sense out of stuff. Intellisense can't tell you much about the scrolling behavior of ListBox when nobody outside that particular ListBox's template at the moment can even guess what that scrolling behavior might be.
So, in answer to your question: Yes. Basically you just have to read a lot of stuff. And keep a "Cool XAML Tricks" text file to note down cool stuff you hear about that you don't have a use for just yet.
But that's the way this profession has always been. Even with Intellisense, you can't use a class you don't know about.
Today I learned about ColumnDefinition.SharedSizeGroup and Grid.IsSharedSizeScope, and I learned that you can derive value converters from MarkupExtension and give them strongly typed, named properties instead of having to pass some goofy string into CommandParameter.
I believe the best docs we have right now are the MSDN documentation pages for WPF controls. For example, if you look up ListBox, you can find information about the attached ScrollViewer, and also it's full default template.
https://msdn.microsoft.com/en-us/library/cc278062(v=vs.95).aspx

Advantage of Binding?

I am not sure that I fully understand the advantage of binding. For example, if I want to bind a string value to a TextBlock I need to do the following:
Create a class that extends INotifyPropertyChanged
Add a string to that class (say: MyString)
Extend the set method for MyString so that it calls another method (say: OnPropertyChanged)
Create the OnPropertyChanged method to call the PropertyChangedEventHandler event
Then I need to create a new instance of the class, set my TextBlock.DataContext to point to that class, and finally add the XAML bit for the binding.
Can someone explain the advantage of this over simply setting:
TextBlock.Text = MyString;
Thanks!
Any changes to MyString won't be automatically reflected in your UI.
Your code behind will be littered with "when this event occurs, update these pieces of data", so you'll essentially be writing your own messy data binding logic for each and every view.
The advantage is that you can both change and display the value in multiple places, without having to update some method to add another TextBlock assignment each time the value changes. Any new display control just binds itself to the property, the rest is automatic.
Now if you really just set the value in one place and show it in one control, then you're right, there's not much point.
The gain of using Data Binding isn't particularly noticeable for a TextBlock binding to a static string.
However if the value of MyString changes during application runtime it becomes much more useful - especially in a case where the object that owns that property is unaware of the TextBlock. This separation between UI and the underlying data layer can be created using a design pattern such as MVVM.
Data Binding is also useful for more complex properties such as Items in a ListBox control. Just bind the ListBox.Items to a property that is of type ObservableCollection and the UI will automatically update whenever the content of that collection changes.

Add DependencyObject to control from code-behind

I want to add a DependencyObject to a control from code behind. I have searched and searched online for how to do this with absolutely no success. The DependencyObject has a DependencyProperty. I also want to set this property from code-behind.
charting:ChartBehaviors inherits directly from DependencyObject. It is a class I wrote myself. The Chart control is a Third-party control.
charting:ChartBehaviors.FloatingTooltip is the DependencyProperty. This is also a class I wrote myself.
Here is what it looks like in XAML. I want to do this in code behind so that I can turn on and off the "behavior".
<charting:Chart>
<charting:ChartBehaviors.FloatingTooltip>
<charting:FloatingTooltipBehavior
TooltipTemplate="{StaticResource tooltipTemplate}" />
</charting:ChartBehaviors.FloatingTooltip>
</charting:Chart>
You can just use the SetValue method on the object (assuming you give your chart an id of chart1).
var behave = new FloatingTooltipBehavior();
chart1.SetValue(ChartBehaviors.FloatingTooltipProperty, behave);
Another solution would be to just add an Enabled property to your behavior and then set that from the code behind.
Well, you basically instantiate necessary dependency object and use appropriate method to add it to the control. For example, to add TextBlock to the StackPanel, you write it like this:
TextBlock txtMyText = new TextBlock();
stackPanel.Children.Add(txtMyText);
If you're wanting to add certain behavior to a chart, you should just get your chart object in code and look for a property like Behaviors or something. Then you either assign a behavior (if it's one-behavior-only) or add it like to the stackpanel:
Chart myChart;
myChart.Behavior = new FloatingTooltipBehavior();
It's hard to tell the exact syntax without knowing the component.

What is the worst gotcha in WPF?

I've started to make myself a list of "WPF gotchas": things that bug me and that I had to write down to remember because I fall for them every time....
Now, I'm pretty sure you all stumbled upon similar situations at one point, and I would like you to share your experience on the subject:
What is the gotcha that gets you all the time? the one you find the most annoying?
(I have a few issues that seem to be without explanation, maybe your submissions will explain them)
Here are a few of my "personnal" gotchas (randomly presented):
For a MouseEvent to be fired even when the click is on the "transparent" background of a control (e.g. a label) and not just on the content (the Text in this case), the control's Background has to be set to "Brushes.Transparent" and not just "null" (default value for a label)
A WPF DataGridCell's DataContext is the RowView to whom the cell belong, not the CellView
When inside a ScrollViewer, a Scrollbar is managed by the scrollviewer itself (i.e. setting properties such as ScrollBar.Value is without effect)
Key.F10 is not fired when you press "F10", instead you get Key.System and you have to go look for e.SystemKey to get the Key.F10
... and now you're on.
Always watch the output window for
binding errors. Ignoring the output
window is a recipe for tears.
Use PresentationTraceOptions.TraceLevel="High" in a binding to get verbose binding information when debugging binding failures.
Make static, unchanging resources such as brushes PresentationOptions:Freeze="True" to save resources at runtime.
Use the WPF DataGrid as a datagrid. Modifying it to behave like Excel is a massive pain in the butt.
BindingList<T> does not play well with CollectionViewSource. Expose ObservableCollection<T> from your viewmodels instead.
The internet supplies half a dozen different ideas for displaying CueBanner text in a WPF textbox. They are all broken.
1) One that used to get me every half an hour when I was making my transition from WinForms: use TextBlock instead of Label when putting random text on the UI (or don't use any tag at all, if the text is static)!
2) DataTriggers/Triggers can't be put into Control.Triggers, but have to go into Control.Styles/Style/Style.Triggers
3) Property's type must implement IList, not IList<T>, if the property is to be recognized by XAML as a collection property.
4) Bindings capture exceptions.
5) Use singleton converters/static converter class, so you don't have to create a new converter every time you use it.
6) A type for default value of DependencyProperty has to be clearly specified: 0u as uint, (float) 0 as float, 0.0 as double...
7) It matters if the control's property definitions are before or after its content.
8) NEVER use PropertyMetadata to set a default value of reference type DependencyProperty. The same object reference will be assigned to all instances of the owning class.
When first starting out, the main gotchas that would get me would be
Lists not updating due to forgetting
to use ObservableCollection
Properties not being updated either
forgetting to add OnPropertyChanged
or incorrectly typing the property
name
Recently I have stumbled across these issues
Application failing to start due to
corrupt font cache
StringFormat localization issues
If enabled, Button.IsCancel assigns false to Window.DialogResult but Button.IsDefault no.
They are so similar and for me it seemed intuitive at first that both should close dialog. I usually break MVVM and fix this in code-behind
Button.IsCancel + Command = Dialog won't close (Window.DialogResult left unassigned) but Command executes
As I understand it: If IsCancel had higher priority than Command then on Esc it would assign 'false' to DialogResult and Command won't be called. Or, if Command would have higher priority then it would be called first and DialogResult would be assigned. I don't understand how it is skipped?
Binding swallows exceptions!
It not only steals time while debugging it is also wrong from the OOP point of view because if exception is thrown it means that something exceptional had happened somewhere in our system (anything from wrong data supply to unauthorized access to memory failure) so it can be handled only if you know what to do. You can't just catch(Exception){} catch 'em all and then ignore. If there is unknown exception in program it should notify user, log and close not pretend like everything is ok...
HeaderContent can have only one child control and has no padding
Everything should have padding even logical controls (containers), right? I think it is inconsistent. What do you think?
If you set focus to ListBox via FocusManager.FocusedElement you still won't be able to switch it's content with keyboard because focus is set to ListBoxes frame not it's content. I think I don't know other UI API that would expose something like controls frame to UI programmer it should be encapsulated from us because abstractly ListBox represents a list, it is just a list of things not a list of things in a box. ok it has box in its name but still... We almost have two different controls here.
MVVM not breaking fix
ListBox.IsSynchronizedWithCurrentItem by default is false so if you assign different value or null to ItesSource then SelectedItem still holds old value until user selects something from a new list. It could mess up CanExecute for example. Need to set it every time by hand.
No binding exposed in PasswordBox results in time waste and dirty hacks... But still it has a string property PasswordBox.Password exposed so don't even try to argue about security because Snoop...
It is not a gotcha but table layout is so IE6 IMO. Container design helps separate content from its layout.
Because every time I need to change something in places I need to mess up with Grid.Row and Grid.Column. Yes, we have DockPanel, StackPanel and others but you can't do some column alignment inside of them. (And DockPanel is like completely separate gotcha) If UniformGrid would be more customizable it would be ideal I think. You always need to choose between Grid and Panels and usually if you gain something you loose something else.
I got a pretty nifty one last week:
When Templating a RichTextBox, the event handling inside the template follows a strange route that has nothing to do neither with tunnelling nor bubbling
e.g.: In the case of an event that is supposed to tunnel: the event first tunnels through the ContentPresenter, then it tunnels back from the top of the template.
see my question on the subject
ToolTips and ContextMenus not sharing the DataContext of its owner? I think that gets everyone at first
There is no clean way to handle validation in WPF, I am not a fan of magic string which IDataErrorInfo offers by default:
public string this[string columnName]
{
if (columnName == "FirstName")
{
if (string.IsNullOrEmpty(FirstName))
result = "Please enter a First Name";
}
}
However, I have tried many frameworks like SimpleMVVM, FluentValidation and MVVMValidation and BY FAR MVVM Validation is the best getting to do stuff like:
Validator.AddRule(() => RangeStart,
() => RangeEnd,
() => RuleResult.Assert(RangeEnd > RangeStart, "RangeEnd must be grater than RangeStart");
My personal favorite is this one:
public double MyVariable
{
get { return (double)GetValue(MyVariableProperty); }
set { SetValue(MyVariableProperty, value); }
}
public static readonly DependencyProperty MyVariableProperty = DependencyProperty.Register(
"MyVariable", typeof(double), typeof(MyControl), new UIPropertyMetadata(0));
Try it, once this property is declared it will crash. Why? Because 0 can't be assigned to a double using reflection apparently.
Not really a gotcha but an advice: Use Snoop or something similar, if you don't use it you must be crazy ... Crazy i tell ya!
Binding.StringFormat only works if the type of the target property is string.
TreeView's SelectedItem property is not settable. Instead you have to bind TreeViewItem's IsSelected property to your item's viewmodel and set your selection there.
ListBox's SelectedItem, on the other hand is settable, but item selection is not equal to item focus. If you want to implement proper keyboard navigation along with selecting items from within viewmodel, you have to implement manual focus fix, like:
public void FixListboxFocus()
{
if (lbFiles.SelectedItem != null)
{
lbFiles.ScrollIntoView(lbFiles.SelectedItem);
lbFiles.UpdateLayout();
var item = lbFiles.ItemContainerGenerator.ContainerFromItem(viewModel.SelectedFile);
if (item != null && item is ListBoxItem listBoxItem && !listBoxItem.IsFocused)
listBoxItem.Focus();
}
}
...and call it every time you change selected item from viewmodel:
SelectedFile = files.FirstOrDefault();
viewAccess.FixListboxFocus();

Resources