Given:
<ScrollViewer VerticalScrollBarVisibility="Auto" >
<ItemsControl ItemsSource="{Binding Controls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
Where the "Controls binding" is an ObservableCollection in the viewmodel containing some sort of usercontrols.
As the content in the wrappanel is centered, the current behavior is as follows:
UC A is added to the list, and displayed in the center of the panel.
UC B is added to the list, UC A is moved to the left and UC B is added to the panel.
UC C is added to the list, UC A and B
is moved to the left and UC C is added to the panel.
What I want is to add a "movement" translation/transition when a new usercontrol is added, i.e. I want to show animate the transition of A/B..n the left as each UC is added.
I would prefer to do as much as possible in XAML, and not break the MVVM pattern.
Bonus, I want to be able animate when an UC is removed too.
I consider Controls as an ObservableCollection of FrameworkElement
You can use this code:
<Window x:Class="Marathonbet.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="MainWindow_OnLoaded" Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<Storyboard x:Key="OnLoaded1">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" >
<EasingThicknessKeyFrame KeyTime="0" Value="20,0,0,0"/>
<EasingThicknessKeyFrame KeyTime="0:0:0.4" Value="5"/>
</ThicknessAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" >
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="55"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" >
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnUnloaded1" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)">
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="55"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="0">
<ItemsControl ItemsSource="{Binding Controls}" x:Name="x">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<Button Grid.Row="1" Content="Add" Margin="5" HorizontalAlignment="Left" Padding="15,2,15,2" Click="btAdd_OnClick"/>
<Button Grid.Row="1" Content="Remove" Margin="64,5,0,5" HorizontalAlignment="Left" Padding="15,2,15,2" Click="btRemove_OnClick"/>
</Grid>
</Window>
Code behind
public class MyObservableCollection : ObservableCollection<FrameworkElement>
{
private Storyboard unloadedStoryboard;
public Storyboard UnloadedSotryBoard
{
get { return unloadedStoryboard; }
set
{
unloadedStoryboard = value;
unloadedStoryboard.Completed += UnloadedStoryboardOnCompleted;
}
}
public Storyboard LoadedSotryBoard { get; set; }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement item in e.NewItems)
item.BeginStoryboard(LoadedSotryBoard);
}
base.OnCollectionChanged(e);
}
private HashSet<int> indexesToRemove = new HashSet<int>();
protected override void RemoveItem(int index)
{
indexesToRemove.Add(index);
var item = Items[index];
UnloadedSotryBoard.Begin(item);
}
private void UnloadedStoryboardOnCompleted(object sender, EventArgs eventArgs)
{
foreach (var i in new HashSet<int>(indexesToRemove))
{
base.RemoveItem(i);
indexesToRemove.Remove(i);
}
}
}
public partial class MainWindow
{
public MyObservableCollection Controls { get; set; }
#region Constructors
public MainWindow()
{
Controls = new MyObservableCollection();
InitializeComponent();
Controls.LoadedSotryBoard = (Storyboard) FindResource("OnLoaded1");
Controls.UnloadedSotryBoard = (Storyboard) FindResource("OnUnloaded1");
}
#endregion
#region Events
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
Controls.Add(new MyControl {DataContext = "A"});
Controls.Add(new MyControl {DataContext = "B"});
Controls.Add(new MyControl {DataContext = "C"});
}
private void btAdd_OnClick(object sender, RoutedEventArgs e)
{
Controls.Add(new MyControl {DataContext = (char) new Random().Next(0, Byte.MaxValue)});
}
private void btRemove_OnClick(object sender, RoutedEventArgs e)
{
if (Controls.Count == 0)
return;
Controls.RemoveAt(Controls.Count - 1);
}
#endregion
}
Related
We are trying to set Height and Width of the button based on the window size. We want to achieve this using Visual States. We want to set Storyboard Target to a button which is inside a DataTemplate. Directly setting target by name using StoryBoard.TargetName is not possible because of the namescope of DataTemplate. Is there any way to do this in XAML.
Refer to-
XAML:
<UserControl x:Class="ResizeSampleApp.Controls.ResizeUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ResizeSampleApp.Controls"
mc:Ignorable="d"
SizeChanged="CurrentWindow_SizeChanged"
x:Name="DashBoard"
>
<Grid x:Name="grid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ResizeStates">
<VisualState x:Name="FirstHorizontalBreakpoint">
<Storyboard >
<DoubleAnimation To="116" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height"></DoubleAnimation>
<DoubleAnimation To="182" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width"></DoubleAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="MinSize">
<Storyboard>
<DoubleAnimation To="100" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height"></DoubleAnimation>
<DoubleAnimation To="197" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width"></DoubleAnimation>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Orientation="Vertical">
<Grid Margin="10">
<ItemsControl Name="icTodoList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="TargetBtn" Content="{Binding Title}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
</Grid>
</UserControl>
C#:
public class TodoItem
{
public string Title { get; set; }
}
public partial class ResizeUserControl : UserControl
{
public ResizeUserControl()
{
InitializeComponent();
List<TodoItem> items = new List<TodoItem>();
items.Add(new TodoItem() { Title = "Testing 1 " });
items.Add(new TodoItem() { Title = "Testing 2" });
items.Add(new TodoItem() { Title = "Testing 3" });
icTodoList.ItemsSource = items;
}
public void CurrentWindow_SizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
{
if (this.ActualWidth > 847)
{
VisualStateManager.GoToState(this.DashBoard, "FirstHorizontalBreakpoint", false);
}
else
{
VisualStateManager.GoToState(this.DashBoard, "MinSize", false);
}
}
}
Thanks in Advance.
It is not possible to use visual state animations to animate properties inside a DataTemplate. The DataTemplate is just a template, that is not part of the visual tree, the moment the Storyboard is defined. The template will be duplicated for each templated target e.g each item container. This means there will be multiple buttons with the name TargetBtn - when viewed from outside the template's scope. So you would have to move the animations to the DataTemplate scope, which means inside the template.
In addition to the control's visual state definitions, you should add properties to reflect the visual state as object state.
It is best practice and recommended by Microsoft Docs to do this for each state: add properties like IsMouseOver to indicate the MouseOver state or IsFocused which goes in tandem with the Focused state, just to name some framework examples.
This allows your control to express the visual state as real instance state, which can be consumed outside the context of the control's direct content - visual states are only consumable by the VisualStateManager.
You can then use this usually public read-only dependency properties to trigger e.g. a DataTrigger, which you define inside the DataTemplate to animate the template's content:
ResizeUserControl.xaml.cs
public partial class ResizeUserControl : UserControl
{
#region IsExpandedViewEnabled read-only dependency property
protected static readonly DependencyPropertyKey IsExpandedViewEnabledPropertyKey = DependencyProperty.RegisterReadOnly(
"IsExpandedViewEnabled",
typeof(bool),
typeof(ResizeUserControl),
new PropertyMetadata(default(bool)));
public static readonly DependencyProperty IsExpandedViewEnabledProperty = MyControl.IsExpandedViewEnabledPropertyKey.DependencyProperty;
public bool IsExpandedViewEnabled
{
get => (bool) GetValue(MyControl.IsExpandedViewEnabledProperty);
private set => SetValue(MyControl.IsExpandedViewEnabledPropertyKey, value);
}
#endregion IsExpandedViewEnabled read-only dependency property
public ResizeUserControl()
{
InitializeComponent();
}
#region Overrides of FrameworkElement
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
if (this.ActualWidth > 847)
{
VisualStateManager.GoToState(this, "FirstHorizontalBreakpoint", false);
this.IsExpandedViewEnabled = true;
}
else
{
VisualStateManager.GoToState(this, "MinSize", false);
this.IsExpandedViewEnabled = false;
}
}
}
ResizeUserControl.xaml
<UserControl>
<Grid x:Name="grid">
<StackPanel Orientation="Vertical">
<Grid Margin="10">
<ItemsControl Name="icTodoList"
ItemsSource="{Binding DataItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="TargetBtn" Height="60"
Content="{Binding Title}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=IsExpandedViewEnabled}"
Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="116" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height" />
<DoubleAnimation To="182" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="100" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Height" />
<DoubleAnimation To="197" Storyboard.TargetName="TargetBtn" Storyboard.TargetProperty="Width" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
</Grid>
</UserControl>
I’m new to WPF NotifyIcon and I’m trying to use the Windowless Sample which uses a ResourceDictionary instead of a window and the TaskbarIcon.DataContext is set to my ViewModel. I can call the example commands (ShowWindowCommand, etc.) and it works fine.
However, in my ViewModel I can’t figure out how to reference the TaskbarIcon. I want to show a standard balloon something like NotifyIcon.ShowBallonTip(title, text, BalloonIcon.Error). I’ve tried giving the tb:TaskbarIcon an x:Name but my ViewModel still does not see it.
How do I reference the TaskbarIcon from my ViewModel? Thanks!
<tb:TaskbarIcon x:Key="NotifyIcon"
IconSource="/Red.ico"
ToolTipText="Double-click for window, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}">
<tb:TaskbarIcon.DataContext>
<local:NotifyIconViewModel />
</tb:TaskbarIcon.DataContext>
</tb:TaskbarIcon>
In MVVM you can't directly operate control in your VM.
VM must nothing know about View. Instead of it you must define property Icon in VM (as I see it is NotifyIconViewModel) and then in View (TaskbarIcon) you need bind it to IconSource.
VM:
public Icon { get { new System.Drawing.Icon(#"..\Properties\Icons\YourIcon.ico"); }};
View
<tb:TaskbarIcon DataContext="YourViewModel"
IconSource="{Binding Path=Icon}"
ToolTipText="Double-click for window, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}"/>
Here is one implementation that works fine for me...
MainWindow.xaml:
xmlns:tb="http://www.hardcodet.net/taskbar"
<tb:TaskbarIcon x:Name="TrayIcon" IconSource="icon.ico" ToolTipText="{Binding TrayIconText}" MenuActivation="RightClick"
LeftClickCommand="{Binding TrayClickCommand}">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Header="Exit" Command="{Binding CloseAppCommand}" CommandParameter="{Binding}">
<MenuItem.Icon>
<Image Source="pack://application:,,,/Resources/Img/Shutdown32.png" Style="{StaticResource MenuItemImage}"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
MainWindow.xaml.cs:
//Subscribe to VM event:
var mvm = DataContext as MainViewModel;
mvm.DisplayTrayBalloonNotice += OnDisplayTrayBalloon;
private void OnDisplayTrayBalloon(object sender, NotificationEventArgs<string> e)
{
var balloon = new TrayBalloon { NotificationText = e.Data };
PopupAnimation pa = PopupAnimation.Fade;
TrayIcon.ShowCustomBalloon(balloon, pa, Settings.Default.NotificationDuration * 1000);
}
TrayBalloon.xaml:
<UserControl x:Class="MCPublisher.Resources.TrayBalloon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
Height="150" Width="300">
<UserControl.Resources>
<Storyboard x:Key="FadeIn">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Grid"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="0.95" />
<SplineDoubleKeyFrame KeyTime="00:00:03" Value="0.95" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="HighlightCloseButton">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ImgClose"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.4" />
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="FadeCloseButton">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ImgClose"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1" />
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.4" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="FadeBack">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Grid"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1" />
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="FadeOut" Completed="OnFadeOutCompleted">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Grid"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1" />
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.2" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.Triggers>
<EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonShowing">
<BeginStoryboard Storyboard="{StaticResource FadeIn}" x:Name="FadeInBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="ImgClose">
<BeginStoryboard Storyboard="{StaticResource HighlightCloseButton}" x:Name="HighlightCloseButtonBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="ImgClose">
<BeginStoryboard Storyboard="{StaticResource FadeCloseButton}" x:Name="FadeCloseButtonBeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<StopStoryboard BeginStoryboardName="FadeInBeginStoryboard" />
<BeginStoryboard x:Name="FadeBackBeginStoryboard1" Storyboard="{StaticResource FadeBack}" />
</EventTrigger>
<EventTrigger RoutedEvent="tb:TaskbarIcon.BalloonClosing">
<BeginStoryboard Storyboard="{StaticResource FadeOut}" x:Name="FadeOutBeginStoryboard" />
</EventTrigger>
</UserControl.Triggers>
<Grid x:Name="Grid" MouseEnter="Grid_OnMouseEnter" MouseLeave="Grid_OnMouseLeave">
<Border x:Name="BnBorder" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="1" CornerRadius="5"
BorderBrush="{DynamicResource GridView_FilteringControlOuterBorder}"
Background="{DynamicResource GridView_FilteringControlBackground}">
<Border.Effect>
<DropShadowEffect Color="#FF747474" />
</Border.Effect>
</Border>
<Image HorizontalAlignment="Left" Margin="10 10 0 0" Width="64" Height="64" Stretch="Fill" VerticalAlignment="Top"
Source="{Binding ImageSource}" />
<TextBlock Margin="84 35 10 0" VerticalAlignment="Top" FontSize="16" FontWeight="Bold" Foreground="#FF575757">
<Run Text="VHI Notification"/>
</TextBlock>
<Path Fill="#FFFFFFFF" Stretch="Fill" Margin="84 60 10 0" VerticalAlignment="Top" Height="1"
Data="M26,107 L220.04123,107" SnapsToDevicePixels="True">
<Path.Stroke>
<LinearGradientBrush EndPoint="0.973,0.5" StartPoint="0.005,0.5">
<GradientStop Color="#FF6868FF" Offset="0" />
<GradientStop Color="#346868FF" Offset="1" />
</LinearGradientBrush>
</Path.Stroke>
</Path>
<TextBlock Margin="20 90 10 0" VerticalAlignment="Top" Height="24" TextWrapping="Wrap" FontSize="12" FontWeight="Bold">
<Run Text="⦁ "/>
<Run Text="{Binding NotificationText}"/>
</TextBlock>
<Image x:Name="ImgClose" HorizontalAlignment="Right" Margin="0 10 10 0" VerticalAlignment="Top" Width="16" Height="16"
Stretch="Fill" Opacity="0.4" ToolTip="Close Balloon" Source="pack://application:,,,/Resources/Img/Exit32.png"
MouseDown="ImgClose_OnMouseDown" />
</Grid>
</UserControl>
TrayBalloon.xaml.cs:
public partial class TrayBalloon : UserControl, INotifyPropertyChanged
{
private bool isClosing;
public TrayBalloon()
{
InitializeComponent();
DataContext = this;
TaskbarIcon.AddBalloonClosingHandler(this, OnBalloonClosing);
}
private string imageSource;
public string ImageSource
{
get { return imageSource; }
set
{
imageSource = value;
OnPropertyChanged("ImageSource");
}
}
private string notificationText;
public string NotificationText
{
get { return notificationText; }
set
{
notificationText = value;
OnPropertyChanged("NotificationText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void OnBalloonClosing(object sender, RoutedEventArgs e)
{
e.Handled = true;
isClosing = true;
}
private void OnFadeOutCompleted(object sender, EventArgs e)
{
var pp = (Popup)Parent;
pp.IsOpen = false;
}
private void Grid_OnMouseEnter(object sender, MouseEventArgs e)
{
if (isClosing) return;
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.ResetBalloonCloseTimer();
}
private void Grid_OnMouseLeave(object sender, MouseEventArgs e)
{
if (isClosing) return;
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon();
}
private void ImgClose_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this);
taskbarIcon.CloseBalloon();
}
}
I'm not sure if this is the preferred way to accomplish this in a ViewModel but this is how I got a reference to the TaskbarIcon.
tb = (TaskbarIcon)Application.Current.FindResource("MyTray");
I have a control like below xaml with Read only enabled.
<TextBox Text="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="Transparent" IsReadOnly="True" BorderThickness="0" TextWrapping="Wrap" >
Now when i double click this text box , i should be able to enter a text.
Readonly property should become false
If I move to another item in the window other than this text box , then the text box should become readonly again.
I am trying to do it with Triggers. but not getting the right hint . Can anyone help me here ?
You can make this with 2 events, MouseDoubleClick and LostFocus
<Grid>
<TextBox IsReadOnly="True"
MouseDoubleClick="TextBox_MouseDoubleClick"
LostFocus="TextBox_LostFocus"/>
</Grid>
In you procedural code:
private void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
TextBox textBox = sender as TextBox;
textBox.IsReadOnly = false;
//textBox.CaretIndex = textBox.Text.Count();
textBox.SelectAll();
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
TextBox textBox = sender as TextBox;
textBox.IsReadOnly = true;
}
You can use Style and EventTrigger to do that
<Window xmlns:sys="clr-namespace:System;assembly=mscorlib" ...>
<Window.Resource>
<Style x:Key="MyTextBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<EventTrigger RoutedEvent="LostFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetProperty="(TextBox.IsReadOnly)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>True</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseDoubleClick">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetProperty="(TextBox.IsReadOnly)">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>False</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resource>
...
<TextBox Style="{StaticResource MyTextBoxStyle}" .../>
</Window>
You can use System.Windows.Interactivity assembly (msdn) to do that.
First: create helper class to set properties:
public class SetterAction : TriggerAction<DependencyObject>
{
public SetterAction()
{
Setters = new List<Setter>();
}
public List<Setter> Setters { get; set; }
protected override void Invoke(object parameter)
{
foreach (var item in Setters)
{
AssociatedObject.SetValue(item.Property, item.Value);
}
}
}
XAML:
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="Transparent" IsReadOnly="True" BorderThickness="0" TextWrapping="Wrap"
Height="30" Width="200">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:EventTrigger.Actions>
<local:SetterAction>
<local:SetterAction.Setters>
<Setter Property="TextBox.IsReadOnly" Value="False" />
<Setter Property="TextBox.Background" Value="Green" />
</local:SetterAction.Setters>
</local:SetterAction>
</i:EventTrigger.Actions>
</i:EventTrigger>
<i:EventTrigger EventName="LostFocus">
<i:EventTrigger.Actions>
<local:SetterAction>
<local:SetterAction.Setters>
<Setter Property="TextBox.IsReadOnly" Value="True" />
<Setter Property="TextBox.Background" Value="Red" />
</local:SetterAction.Setters>
</local:SetterAction>
</i:EventTrigger.Actions>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Where i is:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
The best answer is in the form of Attached Dependency Property. Usage:
xmlns:e="clr-namespace:Extensions"
<TextBox e:TextBoxExtensions.IsEditableOnlyOnDoubleClick="True"/>
#nullable enable
namespace Extensions;
public static class TextBoxExtensions
{
#region IsEditableOnlyOnDoubleClick
public static readonly DependencyProperty IsEditableOnlyOnDoubleClickProperty =
DependencyProperty.RegisterAttached(
nameof(IsEditableOnlyOnDoubleClickProperty).Replace("Property", string.Empty),
typeof(bool),
typeof(TextBoxExtensions),
new PropertyMetadata(false, OnIsEditableOnlyOnDoubleClickChanged));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetIsEditableOnlyOnDoubleClick(DependencyObject element)
{
return (bool)element.GetValue(IsEditableOnlyOnDoubleClickProperty);
}
public static void SetIsEditableOnlyOnDoubleClick(DependencyObject element, bool value)
{
element.SetValue(IsEditableOnlyOnDoubleClickProperty, value);
}
private static void OnIsEditableOnlyOnDoubleClickChanged(
DependencyObject element,
DependencyPropertyChangedEventArgs args)
{
if (element is not TextBox textBox)
{
throw new ArgumentException($"{nameof(element)} should be {nameof(TextBox)}.");
}
if (args.OldValue is true)
{
textBox.MouseDoubleClick -= TextBox_MouseDoubleClick;
textBox.LostFocus -= TextBox_LostFocus;
textBox.IsReadOnly = false;
}
if (args.NewValue is true)
{
textBox.MouseDoubleClick += TextBox_MouseDoubleClick;
textBox.LostFocus += TextBox_LostFocus;
textBox.IsReadOnly = true;
}
}
private static void TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender is not TextBox textBox)
{
return;
}
textBox.IsReadOnly = false;
textBox.SelectAll();
}
private static void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is not TextBox textBox)
{
return;
}
textBox.IsReadOnly = true;
}
#endregion
}
I need to show the images continuously like projection flip animation like the below link having sample.
http://www.silverlightbuzz.com/2009/10/14/using-the-3d-tools-to-animate-in-blend/
How to implement the above sample second animation .There are two types of animations we can see in above link .How to implement second one in windows 8 like that?
The dlls System.Windows.Interactivity and Microsoft.Expression.Interactions are n't useful in windows 8.Then how to do this animation in windows 8?
EDIT:
<UserControl.Resources>
<Storyboard x:Name="Storyboard1" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:01.5000000" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="90"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle1" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="-90"/>
<EasingDoubleKeyFrame KeyTime="00:00:02" Value="-90"/>
<EasingDoubleKeyFrame KeyTime="00:00:02.5000000" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:03.5000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle1" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)">
<EasingDoubleKeyFrame KeyTime="00:00:03.5000000" Value="0"/>
<EasingDoubleKeyFrame KeyTime="00:00:04" Value="90"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="-90"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="Black">
<Rectangle x:Name="rectangle1" RadiusX="12" RadiusY="12" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center" Width="300">
<Rectangle.Projection>
<PlaneProjection RotationX="-90"/>
</Rectangle.Projection>
<Rectangle.Fill>
<ImageBrush ImageSource="/Assets/7.png"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="rectangle" RadiusX="12" RadiusY="12" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center" Width="300">
<Rectangle.Projection>
<PlaneProjection RotationX="-90"/>
</Rectangle.Projection>
<Rectangle.Fill>
<ImageBrush ImageSource="/Assets/8.png"/>
</Rectangle.Fill>
</Rectangle>
<Button x:Name="StartAnimation"
Content="Start Animation"
Grid.Row="1"
Width="163"
Height="61" Margin="0,65,0,24" Click="StartAnimation_Click" />
</Grid>
link that you should follow..it will tell you how make storyboard for animation ..and when you save your storyboard in blend then a stoyboard will automatically come in page.resources section and you can begin it in your code behind whereever you want to..just right''
StoryboardName.begin();
i ma giving you this link it is very difficult to give you all information step by step as it requires ui use..hope this helps you..
ok fine i have done something that might helps you..like i have binded your rectangle fill to a property that you can change at any time through code .like you can use dispatchertimer and after fixed interval you can change the image of your rectangle like this..
<Rectangle x:Name="rectangle1" RadiusX="12" RadiusY="12" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center" Width="300">
<Rectangle.Projection>
<PlaneProjection RotationX="-90"/>
</Rectangle.Projection>
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding hello1}"/>
</Rectangle.Fill>
</Rectangle>
in your page .cs do this..
public sealed partial class MainPage : Page , INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
DispatcherTimerSetup();
// hello1 = "/Assets/4.jpeg";
hello1 = new ImageBrush();
hello1.ImageSource =
new BitmapImage(
new Uri(BaseUri, "Assets/1.jpg")
);
this.DataContext = this;
}
DispatcherTimer dispatcherTimer;
DateTimeOffset startTime;
DateTimeOffset lastTime;
DateTimeOffset stopTime;
int timesTicked = 1;
int timesToTick = 10;
public void DispatcherTimerSetup()
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
//IsEnabled defaults to false
// TimerLog.Text += "dispatcherTimer.IsEnabled = " + dispatcherTimer.IsEnabled + "\n";
startTime = DateTimeOffset.Now;
lastTime = startTime;
// TimerLog.Text += "Calling dispatcherTimer.Start()\n";
dispatcherTimer.Start();
//IsEnabled should now be true after calling start
// TimerLog.Text += "dispatcherTimer.IsEnabled = " + dispatcherTimer.IsEnabled + "\n";
}
void dispatcherTimer_Tick(object sender, object e)
{
rectangle.Fill = hello1;
}
private ImageBrush hello;
public ImageBrush hello1
{
get
{
return hello;
}
set
{
hello = value;
FirePropertyChanged("hello1");
}
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
private void StartAnimation_Click_1(object sender, RoutedEventArgs e)
{
Storyboard1.Begin();
// Storyboard1.GetCurrentTime =
// Storyboard1.
// double sd = Storyboard1.GetCurrentTime;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
you can modify it according to your need..hope this will help you..
I am a beginner in WPF.
I want to display my form on every tick.
but it's only displaying once.
when I debug this, it's hitting the this.topmost=true in timer tick event, but
it is not displaying the window.
I am not sure what's wrong with this code.
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 2, 0);
timer.Tick += tick;
timer.Start();
}
private void tick(object sender, EventArgs e)
{
this.Topmost = true;//display the form
this.Show();
}
}
<Border BorderThickness="1" Background="Beige" BorderBrush="Black" CornerRadius="10">
<StackPanel Margin="20">
<CheckBox Content="Checkable" Margin="5 5 0 5" />
<Button Content="Clickable" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<!-- Animation -->
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:8" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
This should do what you want. I have taken the WPF window that you wanted to show to your client and moved it into another form. I then created another startup form that is hidden and runs the timer showing your animation periodically.
Startup Form:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="10" Width="10" ShowInTaskbar="False" Visibility="Hidden" >
<Grid />
</Window>
Startup Form Code Behind
public partial class MainWindow : Window
{
Window1 cyclicWindow;
public MainWindow()
{
InitializeComponent();
cyclicWindow = new Window1();
cyclicWindow.Show();
cyclicWindow.Topmost = true;
System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 20, 0);
timer.Tick += tick;
timer.Start();
}
private void tick(object sender, EventArgs e)
{
if (cyclicWindow != null)
{
cyclicWindow.Close() ;
}
cyclicWindow = new Window1();
cyclicWindow.Show();
cyclicWindow.Topmost = true;
}
}
Cycling Window:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="200" Width="300" Name="Main"
ShowInTaskbar="False" WindowStyle="None" Background="Transparent" >
<Grid Name="Base" Height="112">
<Border BorderThickness="1" Background="Beige" BorderBrush="Black" CornerRadius="10">
<StackPanel Margin="20">
<CheckBox Content="Checkable" Margin="5 5 0 5" />
<Button Content="Clickable" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<!-- Animation -->
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Main" Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.0" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Main" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:8" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
</Window>
Cycling Window Code Behind:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
AllowsTransparency = true;
}
}