I've made a click event/method that alters the opacity and IsEnabled properties of a textbox.
private void EditButton(object sender, RoutedEventArgs e)
{
religionTB.IsEnabled = true;
DoubleAnimation fade = new
DoubleAnimation(1,TimeSpan.FromSeconds(0.2));
religionTB.BeginAnimation(OpacityProperty, fade);
}
In my WPF project, there are multiple textboxes, I'd like to apply this method to all these textboxes without having to list all of them in the method. How would I go by this?
You can achieve this by using a Style. To do so, go to the context of your event handler (Control or Window) and add a DependencyProperty, to flag the enabled mode and bind a ToggleButton (the edit button) to it that sets this property to enable/ disable the controls and to trigger a fade in and fade out animation:
In your control:
public static readonly DependencyProperty IsEditEnabledProperty = DependencyProperty.Register(
"IsEditEnabled",
typeof(bool),
typeof(MainWindow),
new PropertyMetadata(default(bool)));
public bool IsEditEnabled { get { return (bool) GetValue(MainWindow.IsEditEnabledProperty); } set { SetValue(MainWindow.IsEditEnabledProperty, value); } }
In your XAML add the TextBox style and link a ToggleButton to IsEditEnabled:
<Window.Resources>
<Style x:Key="OpacityStyle" TargetType="TextBox">
<Setter Property="Opacity" Value="0" />
<Setter Property="IsEnabled"
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled}"
Value="True">
<! Fade in animation -->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
BeginTime="0:0:0"
From="0"
To="1"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<! Fade out animation -->
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
BeginTime="0:0:0"
From="1"
To="0"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<ToggleButton x:Name="EditButton" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=IsEditEnabled, Mode=TwoWay}" />
<TextBox x:Name="AnimatedTextBox" Style="{StaticResource TextBoxAnimationStyle}" >
<TextBox x:Name="AnotherAnimatedTextBox" Style="{StaticResource TextBoxAnimationStyle}" >
<TextBox x:Name="NonanimatedTextBox" >
</StackPanel>
</Grid>
If you make the Style implicit by removing the x:Key attribute, it will apply to all TextBox elements within the scope
Related
I have a Tabcontrol that I'm trying to add actions based on triggers.
Using Routed Events I can get my storyboard to trigger and the my actions respond as intended. Now I'm trying to prevent my storyboard actions from triggering for a specific tab header if that header is selected.
This has forced me to move away from routed events since I can't use a property condition and a routed event to determine if my storyboard will execute.
Fast forward and now I have finally been able to get IsMouseOver to respond as I wish to the mouse. (My background properties changes and changes back as the mouse enters and leaves the tabitem header. I'm almost there I think but as soon as I add the exact same storyboard as before my code decides to be lazy and skip it for some reason. The background setter is still being triggered but the storyboard remains silent.
I tried removing the setter but the storyboard still does not trigger.
End of the Day: I looking to set styles that transition nicely between each other for all possible combinations of Is Mouse Over and Is Selected.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components"
xmlns:controls="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components">
<Style TargetType="{x:Type controls:NavigationElement}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:NavigationElement}">
<Border BorderBrush="Black" BorderThickness=".7" x:Name="Bd">
<Grid Width="150" Height="50" Opacity="0.75" x:Name="NavigationElementGrid">
<Grid.Background >
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="0.5" RadiusY="2.0">
<GradientStop Color="#006A4D" Offset="1.0"/>
<GradientStop Color="#56be88" Offset=".2"/>
</RadialGradientBrush >
</Grid.Background>
<ContentControl Grid.Column="0" Content="{TemplateBinding Icon}"/>
<TextBlock x:Name="NavigationElementText" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{TemplateBinding LabelText}" FontFamily="Futura XBlkIt BT" FontSize="12"/>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
</Grid>
</Border>
<!--
Trigger related to when the mouse is over the header
I would like it to Execute the doubleanimations
-->
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<!-- Start BUG the bellow code does not execute -->
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText" Storyboard.TargetProperty="FontSize" To="14" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<!-- End BUG -->
<!-- The triggers are fireing becuase this is being set. -->
<Setter Property="Background" TargetName="Bd" Value="Blue"/>
</MultiTrigger>
<!--
When the mouse leaves I want it to return to it's original
state.
-->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<!-- Start BUG the bellow code does not execute -->
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid" Storyboard.TargetProperty="Opacity" To=".75" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText" Storyboard.TargetProperty="FontSize" To="12" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<!-- End BUG -->
<!-- The triggers are fireing becuase this is being set. -->
<Setter Property="Background" TargetName="Bd" Value="Red"/>
</MultiTrigger>
<!-- Missing two more MultiTriggers (very similar to above) for the cases of if the tab is selected.-->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Code behind
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Tenant_Tool_Analytics_Module.Resources.Components
{
public class NavigationElement : Control
{
public string LabelText
{
get
{
return (string)GetValue(LabelTextProperty);
}
set
{
SetValue(LabelTextProperty, value);
}
}
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(NavigationElement), new PropertyMetadata(string.Empty));
public object Icon
{
get
{
return (object)GetValue(IconProperty);
}
set
{
SetValue(IconProperty, value);
}
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(object), typeof(NavigationElement), new PropertyMetadata(string.Empty));
public System.Windows.Media.Brush BackgroundColour
{
get
{
return (System.Windows.Media.Brush)GetValue(BackgroundColourProperty);
}
set
{
SetValue(BackgroundColourProperty, value);
}
}
public static readonly DependencyProperty BackgroundColourProperty =
DependencyProperty.Register("BackgroundColour", typeof(System.Windows.Media.Brush), typeof(NavigationElement), new PropertyMetadata(Brushes.Black));
}
}
Implementation code
<Window x:Class="Tenant_Tool_Analytics_Module.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Tenant_Tool_Analytics_Module"
xmlns:views="clr-namespace:Tenant_Tool_Analytics_Module.Views"
xmlns:controls="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel LastChildFill="True">
<views:HeaderView x:Name="HeaderView" DockPanel.Dock="Top"/>
<TabControl DockPanel.Dock="Left" TabStripPlacement="Left" Margin="0, 0, 0, 10">
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Stacking Plan" Icon="{StaticResource StackPlanIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Tenant Profile" Icon="{StaticResource TenantProfileIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Submarket" Icon="{StaticResource SubmarketIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Industry" Icon="{StaticResource IndustryIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
</TabControl>
</DockPanel>
</Window>
I think you weren't aware of the fact that MultiDataTrigger objects have both EnterActions and ExitActions. Instead of having one MultiDataTrigger triggering on true with only EnterActions and another MultiDataTrigger triggering on false with only EnterActions, you can use only one MultiDataTrigger triggering on true with both EnterActions (to change the object to an abnormal state) and ExitActions to transition it back to its normal state.
The Triggers collection now works as expected and becomes, as a bonus, easier to read:
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText"
Storyboard.TargetProperty="FontSize"
To="14" Duration="0:0:0.3"/>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BorderBackgroundBrush"
Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Blue"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid"
Storyboard.TargetProperty="Opacity"
To=".75"
Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText"
Storyboard.TargetProperty="FontSize"
To="12"
Duration="0:0:0.3"/>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BorderBackgroundBrush"
Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<!-- Missing two more MultiTriggers (very similar to above) for the cases of if the tab is selected.-->
</ControlTemplate.Triggers>
Also notice how I've used a ColorAnimationUsingKeyFrames to change the Border.Background property without needing a Setter in another Trigger. This way, all changes are performed in the same Storyboard. For this to work, you just need to assign a named SolidColorBrush to your "Bd" Border:
<Border.Background>
<SolidColorBrush x:Name="BorderBackgroundBrush" Color="Red"></SolidColorBrush>
</Border.Background>
To prevent the Storyboard to play if the ancestor TabItem is selected, I suggest you add a boolean IsSelected DependencyProperty to your NavigationElement, so that you can bind this property to its TabItem ancestor by adding a Setter in your Style like this:
<Setter Property="IsSelected" Value="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"/>
And you just have to add the condition in your MultiDataTrigger (but you already figured that out):
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSelected" Value="False"/>
Sidenote: I recommend you wrap and indent your XAML attributes to avoid long XAML lines that force you to scroll. Besides the increased readability, having each XAML attribute on a new line is more version control-friendly because one attribute change only impacts one line.
I am creating a Style containing Animation's, which can then be inherited to specific control styles, which are applied automatically.
Actually I'm struggling on implementing a simple Animation:
When Visibility is changed to Visible, Opacity is changed from 0 to 1
When Visibility is changed to something else than visible, Opacity is doing the inverse thing
So far I got:
<Style x:Key="BaseAnimationsStyle">
<Style.Triggers>
<Trigger Property="FrameworkElement.Visibility" Value="Visible">
<Trigger.EnterActions> <!-- this works -->
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions><!-- this doesn't -->
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
If the above Style is set on a control it then has the following behaviour:
When the control's Visibility is set to Visible the transition is working correctly, meaning it is fading in.
Issue:
When the control's Visibility is set to Hidden (or even Collapsed), the control will be hidden instantly without fading.
My guess is, that there is some default behaviour to be overridden of how FrameworkElement's deal with Visibility-Changes.
Setting the Visibility property to Collapsed or Hidden will make the element invisible right away but instead of setting the Visibility property you could set some attached property of yours and then animate the Opacity property to fade out the element. Please refer to the following link for more information and an example.
WPF: How To Animate Visibility Property?: http://blogs.microsoft.co.il/arik/2010/02/08/wpf-how-to-animate-visibility-property/ https://www.codeproject.com/Articles/57175/WPF-How-To-Animate-Visibility-Property
WPF Fade Animation
I have solved my issue with the information provided by #mm8's Answer.
Basically I added the VisibilityAnimation Class to my Project. I then simply created my Base-Style using the Provided Attached-Property inside a Setter.
<!-- Animations -->
<Style x:Key="BaseAnimationsStyle">
<Setter Property="anim:VisibilityAnimation.AnimationType" Value="Fade" />
</Style>
<!-- This adds the Visibility Animation to every Grid which has access to this resource -->
<Style TargetType="{x:Type Grid}" BasedOn="{StaticResource BaseAnimationsStyle}" />
Another Solution:
Create AttachedProperties for Visiblity. This enables you to set the Visibility of any control without having the Opacity set automatically.
If you want to use Bindings on the property, the first evaluation of the binding causes an animation, if the value is not Visibility.Visible. This is why another Property is needed, to specify another Visibility to start.
public static class AnimateableVisibility
{
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached(
"Visibility", typeof(Visibility), typeof(AnimateableVisibility), new PropertyMetadata(default(Visibility), VisibilityPropertyChanged));
private static void VisibilityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var val = (Visibility) e.NewValue;
// Set StartVisibility to Visible when Visibility is set to Visible
if (val == Visibility.Visible)
d.SetCurrentValue(StartVisibilityProperty, val);
}
public static readonly DependencyProperty StartVisibilityProperty = DependencyProperty.RegisterAttached(
"StartVisibility", typeof(Visibility), typeof(AnimateableVisibility), new PropertyMetadata(default(Visibility)));
public static Visibility GetVisibility(DependencyObject obj)
{
return (Visibility)obj.GetValue(VisibilityProperty);
}
public static void SetVisibility(DependencyObject obj, Visibility value)
{
obj.SetValue(VisibilityProperty, value);
}
public static Visibility GetStartVisibility(DependencyObject obj)
{
return (Visibility)obj.GetValue(VisibilityProperty);
}
public static void SetStartVisibility(DependencyObject obj, Visibility value)
{
obj.SetValue(VisibilityProperty, value);
}
}
Now you can use those properties as follows:
<Grid>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal">
<Button Margin="5,0,15,0" Padding="7,0" Style="{StaticResource VisibilityAnimation}" utils:AnimateableVisibility.StartVisibility="Hidden"
utils:AnimateableVisibility.Visibility="{Binding ElementName=CheckBox, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">I'm a Button</Button>
<CheckBox VerticalAlignment="Center" IsChecked="False" x:Name="CheckBox"></CheckBox>
</StackPanel>
</Grid>
By changing StartVisibility to Visible, you can see how the Button fades out on startup.
all thats missing now is the applied Style:
<Style x:Key="VisibilityAnimation">
<Style.Triggers>
<Trigger Property="utils:AnimateableVisibility.StartVisibility" Value="Hidden">
<!-- This avoids the Animation in cases when the first evaluation of AnimateableVisibility.Visibility is false -->
<Setter Property="UIElement.Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="utils:AnimateableVisibility.Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation To="1" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation To="0" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
I have a Control that can be moved on by dragging. when i drag the control i have a code behind that changes a DependencyProperty that a TranslateTransform is bound to.
now i need to add a button that when is pressed it moves the control, and needs to update the DependencyProperty. I can move the control but can't figure out how to update the DependencyProperty.
code behind:
public partial class AirspeedIndicatorView : UserControl
{
public static readonly DependencyProperty WantedValueProperty =
DependencyProperty.Register("WantedValue", typeof(double), typeof(AirspeedIndicatorView),
new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WantedPropertyChanged));
public double WantedValue
{
get { return (double)GetValue(WantedValueProperty); }
set { SetValue(WantedValueProperty, value); }
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
WantedValue += e.VerticalChange;
}
}
XAML:
<Thumb Canvas.Top="-6" Height="12" Width="16" DragDelta="Thumb_DragDelta" x:Name="WantedThumb">
<Thumb.RenderTransform>
<TranslateTransform Y="{Binding WantedValue,ElementName=View}" />
</Thumb.RenderTransform>
</Thumb>
<Button Padding="1" Margin="1">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="OverridesDefaultStyle" Value="False" />
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="MoveWanted">
<Storyboard Target="{x:Reference WantedThumb}" TargetProperty="RenderTransform.Y" AutoReverse="False">
<DoubleAnimation BeginTime="00:00:00" Duration="0:0:0" By="-1" />
<DoubleAnimation BeginTime="00:00:00.5" Duration="0:0:1.5" By="-15" />
<DoubleAnimation BeginTime="00:00:02" Duration="0:0:1" By="-20" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsPressed" Value="False">
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="MoveWanted" />
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I don't have time to check it now, but you should be able to use the ObjectAnimationUsingKeyFrames class to update your DependencyProperty:
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Saved">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Double>10.0</System:Double>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
Actually, the DoubleAnimationUsingKeyFrames class might be better for you as it is intended to work with doubles, but the only down side is that I don't believe that you can data bind the numerical value, so it would have to be a hard coded value. From the last linked page:
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="AnimatedTranslateTransform"
Storyboard.TargetProperty="X"
Duration="0:0:6"
RepeatBehavior="Forever">
<!-- Using a LinearDoubleKeyFrame, the rectangle moves
steadily from its starting position to 500 over
the first 3 seconds. -->
<LinearDoubleKeyFrame Value="500" KeyTime="0:0:3" />
<!-- Using a DiscreteDoubleKeyFrame, the rectangle suddenly
appears at 400 after the fourth second of the animation. -->
<DiscreteDoubleKeyFrame Value="400" KeyTime="0:0:4" />
<!-- Using a SplineDoubleKeyFrame, the rectangle moves
back to its starting point. The
animation starts out slowly at first and then speeds up.
This KeyFrame ends after the 6th
second. -->
<SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:6" />
</DoubleAnimationUsingKeyFrames>
My application (MVVM Light) resizes it's main window (hides and shows it with an animation). For the animation I use a DataTrigger with parameters from StaticResources:
<Window.Resources>
<system:Double x:Key="WindowMaxWidth">400</system:Double>
<system:Double x:Key="WindowMinWidth">25</system:Double>
</Window.Resources>
<Window.Style>
<Style TargetType="Window">
<Style.Triggers>
<DataTrigger Binding="{Binding DropBox.IsShown}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{StaticResource WindowMaxWidth}"
Duration="0:0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{StaticResource WindowMinWidth}"
Duration="0:0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Style>
In my ViewModel I need my window's width value, so I bound it. The problem is that it's 0 by default, so I have to initialize it with a value. Actually what need is the value form my static resources: WindowMaxWidth.
I can't move the value of WindowMaxWidth to ViewModel because DataTriggr doesn't accept bindings (it complains about threads)
I don't want to keep the same value separately in StaticResources and ViewModel to avoid incoherence.
What should I do?
Put WindowMaxWidth and WindowMinWidth in your viewmodel and reference them with x:Static:
namespace MyNamespace
{
class ViewModel
{
public static double WindowMaxWidth = 400;
public static double WindowMinWidth = 25;
}
}
Import the right namespace xmlns:myns="clr-namespace:MyNamespace"
<DoubleAnimation Storyboard.TargetProperty="Width"
To="{x:Static myns:ViewModel.WindowMaxWidth}"
Duration="0:0:0:0.2"/>
You can use code behind in such way (for instance in constructor, after you set DataContext to ViewModel):
(this.DataContext as MyViewModel).MyWindowWidth = (double)this.FindResource("WindowMaxWidth");
I'm using the following Class and DependencyProperty to allow a style to set an image for a Button:
public static class ImageButton
{
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached("Image", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata((ImageSource)null));
public static ImageSource GetImage(DependencyObject obj)
{
return (ImageSource)obj.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject obj, ImageSource value)
{
obj.SetValue(ImageProperty, value);
}
}
I've defined the following Style (in ImageButton.xaml):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vcontrols="clr-namespace:Vialis.Led.LedControl5.Controls">
<ControlTemplate x:Key="ImageButtonTemplate" TargetType="Button">
<Image Source="{Binding Path=(vcontrols:ImageButton.Image),
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Button}}}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Stretch="Fill"
RenderTransformOrigin="0.5, 0.5">
<Image.Resources>
<Storyboard x:Key="ShrinkStoryboard">
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="0.8"
Duration="0:0:0.15"
AutoReverse="False"/>
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleY)"
To="0.8"
Duration="0:0:0.15"
AutoReverse="False"/>
</Storyboard>
<Storyboard x:Key="GrowStoryboard">
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleX)"
To="1.0"
Duration="0:0:0.15"
AutoReverse="False"/>
<DoubleAnimation Storyboard.TargetName="ImageScale"
Storyboard.TargetProperty="(ScaleTransform.ScaleY)"
To="1.0"
Duration="0:0:0.15"
AutoReverse="False"/>
</Storyboard>
</Image.Resources>
<Image.RenderTransform>
<ScaleTransform x:Name="ImageScale" ScaleX="1" ScaleY="1" CenterX="1" CenterY="1"/>
</Image.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Pressed" Storyboard="{StaticResource ShrinkStoryboard}"/>
<VisualState x:Name="MouseOver" Storyboard="{StaticResource GrowStoryboard}"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Image>
</ControlTemplate>
<Style x:Key="ImageButtonStyle" TargetType="Button">
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Template" Value="{StaticResource ImageButtonTemplate}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
And finally in order to use it I have something like this:
<Button Width="32"
Height="32"
Style="{StaticResource ImageButtonStyle}"
vcontrols:ImageButton.Image="/Images/someimage.png"/>
When I compile and execute the application everything works just fine.
The button gets an image and uses the animations defined in the Style.
At design time however, Visual Studio cannot seem to visualize it and
the XAML editor shows squiggly lines under the entire button definition.
The error information says:
Prefix 'vcontrols' does not map to a namespace.
It's refering to the use of vcontrols in the Style.
If you change the name there, the error will change as well,
so it's not related to the name chosen in the Window/UserControl that is using the Button.
What might be causing this and is there a way to fix it so it works at design time as well?
Update 2:
This issue was only with VS2012 designer (in VS2010 it works fine) and it is already fixed in Visual Studio 2012 Update 3 patch.