Need to create a delay between two effects in Wpf - c#

I am new to WPF. I am creating a animation for sample online test result. Where I like to show No of attended question, No of correct answer as animation. I need to create small delay between AttendedQuestionEffect() and RightAnswerEffect();
CodeBehind code here
public int TotalNoQuestion { get; set; }
public int NoOfQuestionAttended { get; set; }
public int NoOfRightAnswer { get; set; }
public Window1()
{
InitializeComponent();
TotalNoQuestion = 100;
NoOfQuestionAttended = 18;
NoOfRightAnswer = 10;
stkpnl.Background = CreateLinearGradientBrush();
Storyboard strBrd = new Storyboard();
strBrd.Completed += new EventHandler(strBrd_Completed);
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 10;
myDoubleAnimation.To = (TotalNoQuestion *15);
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(2));
Storyboard.SetTargetName(myDoubleAnimation, stkpnl.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(StackPanel.HeightProperty));
strBrd.Children.Add(myDoubleAnimation);
strBrd.Begin(stkpnl);
}
void strBrd_Completed(object sender, EventArgs e)
{
for (int i = 1; i < TotalNoQuestion; i++)
{
Border brd = new Border();
brd.BorderBrush = Brushes.Black;
brd.BorderThickness = new Thickness(1.0);
TextBlock txt = new TextBlock();
txt.Text = i.ToString();
txt.Height = 15;
txt.Width = 200;
brd.Child = txt;
txt.FontSize = 12;
txt.Foreground = Brushes.Black;
stkpnl.Children.Add(brd);
txt.Background = CreateLinearGradientBrush();
txt.Tag = i.ToString();
}
AttendedQuestionEffect();
// Here i need to create delay.
RightAnswerEffect();
}
void AttendedQuestionEffect()
{
int index = 1;
UIElementCollection ulCollection = stkpnl.Children;
foreach (UIElement uiElement in ulCollection)
{
if (index <= NoOfQuestionAttended)
{
Border brd = (Border)uiElement;
TextBlock txt = (TextBlock)brd.Child;
txt.Background = BlinkEffect(Colors.Blue, Colors.SteelBlue, 3000);
brd.Child = txt;
}
index++;
}
}
void RightAnswerEffect()
{
int index = 1;
UIElementCollection ulCollection = stkpnl.Children;
foreach (UIElement uiElement in ulCollection)
{
if (index <= NoOfRightAnswer)
{
Border brd = (Border)uiElement;
TextBlock txt = (TextBlock)brd.Child;
txt.Background = BlinkEffect(Colors.Red, Colors.Blue, 1500);
brd.Child = txt;
}
index++;
}
}
private LinearGradientBrush CreateLinearGradientBrush()
{
LinearGradientBrush brush = new LinearGradientBrush();
brush.StartPoint = new Point(0, 0);
brush.EndPoint = new Point(1, 1);
brush.GradientStops.Add(new GradientStop(Colors.LightCoral, 0.1));
brush.GradientStops.Add(new GradientStop(Colors.YellowGreen, 0.35));
brush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.86));
return brush;
}
private SolidColorBrush BlinkEffect(Color startColor, Color endColor,int time)
{
ColorAnimation myColorAnimation = new ColorAnimation();
myColorAnimation.From = startColor;
myColorAnimation.To = endColor;
myColorAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(time));
myColorAnimation.AutoReverse = true;
myColorAnimation.RepeatBehavior = RepeatBehavior.Forever;
SolidColorBrush myBrush = new SolidColorBrush();
myBrush.BeginAnimation(SolidColorBrush.ColorProperty, myColorAnimation);
return myBrush;
}
**Xaml code here..**
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" Grid.Column="1" BorderThickness="1" Width="200" VerticalAlignment="Bottom" HorizontalAlignment="Left">
<StackPanel x:Name="stkpnl" Grid.Column="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Background="BlueViolet" Width="200" MaxHeight="450" >
</StackPanel>
</Border>
</Grid>
`

Your should look up storyboard animations using KeyFrames e.g. here.
In Xaml this could look like the following ... you just have to translate it to code and adjust it to your needes:
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0.5" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
This sample tfirst fades out an control and then hides it. You can ad a delay by modifying the BeginTime attribute.

Related

Animating the translation of a programmatically created xaml object

Initially I did it directly on nInput and everything worked.
Then I tried to move it to a cloned object (cloneInputForAnimation) but I get this error:
System.InvalidOperationException: The name 'TextBoxAnim' could not be
found in the namespace of 'System.Windows.Controls.TextBox'
XAML:
<TextBox x:Name="nInput" MaxLength="17" PreviewTextInput="numberValidationTextBox" Grid.Column="0"
Height="80" Width="240"
Foreground="White" Background="#202020"
HorizontalAlignment="Left" BorderThickness="0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
FontSize="25">
<TextBox.RenderTransform>
<TranslateTransform x:Name="TextBoxAnim" />
</TextBox.RenderTransform>
<TextBox.Resources>
<Storyboard x:Key="myStoryboardY" x:Name="myStoryboardY">
<DoubleAnimation
Storyboard.TargetName="TextBoxAnim"
x:Name="myDoubleAnimationY"
Storyboard.TargetProperty="Y"
Duration="0:0:0.8"
/>
</Storyboard>
<Storyboard x:Key="myStoryboardX" x:Name="myStoryboardX">
<DoubleAnimation
Storyboard.TargetName="TextBoxAnim"
x:Name="myDoubleAnimationX"
Storyboard.TargetProperty="X"
Duration="0:0:0.8"
/>
</Storyboard>
<Storyboard x:Key="myStoryboardWidth" x:Name="myStoryboardWidth">
<DoubleAnimation
Storyboard.TargetName="nInput"
x:Name="myDoubleAnimationWidth"
Storyboard.TargetProperty="(TextBox.Width)"
Duration="0:0:0.8"
/>
</Storyboard>
<Storyboard x:Key="myStoryboardHeight" x:Name="myStoryboardHeight">
<DoubleAnimation
Storyboard.TargetName="nInput"
x:Name="myDoubleAnimationHeight"
Storyboard.TargetProperty="(TextBox.Height)"
Duration="0:0:0.8"
/>
</Storyboard>
</TextBox.Resources>
</TextBox>
C#:
//I clone the input box to perform the animation
TextBox cloneInputForAnimation = CloneXaml(nInput);
UserInteraction.Children.Add(cloneInputForAnimation);
//Removing text from the realInput
nInput.Text = "";
//nInput.IsEnabled = false;
//Creating a new object which is the one that will remain after the animation
TextBox targetInput = CloneXaml(cloneInputForAnimation);
targetInput.Visibility = Visibility.Hidden;
targetInput.Width = Double.NaN;
targetInput.Height = 50;
nList.Children.Add(targetInput);
//Obtaining the destination point
Point targetPosition = targetInput.TransformToAncestor(rootView).Transform(new Point(0, 0));
//Obtaining the current point
Point currentPos = cloneInputForAnimation.TransformToAncestor(rootView).Transform(new Point(0, 0));
//Calculating the translation to do to reach targetPosition
Vector translateToDo = targetPosition - currentPos;
//Obtaining storyboard and anim for x, y, width, height of cloneInputForAnimation (I can't use x:Name because is an element created programmatically)
Storyboard myStoryY = cloneInputForAnimation.Resources["myStoryboardY"] as Storyboard;
Storyboard myStoryX = cloneInputForAnimation.Resources["myStoryboardX"] as Storyboard;
Storyboard myStoryHeight = cloneInputForAnimation.Resources["myStoryboardHeight"] as Storyboard;
Storyboard myStoryWidth = cloneInputForAnimation.Resources["myStoryboardWidth"] as Storyboard;
DoubleAnimation myAnimY = myStoryY.Children[0] as DoubleAnimation;
DoubleAnimation myAnimX = myStoryX.Children[0] as DoubleAnimation;
DoubleAnimation myAnimHeight = myStoryHeight.Children[0] as DoubleAnimation;
DoubleAnimation myAnimWidth = myStoryWidth.Children[0] as DoubleAnimation;
myAnimY.To = translateToDo.Y;
myAnimX.To = translateToDo.X;
myAnimHeight.To = targetInput.ActualHeight;
myAnimWidth.To = targetInput.ActualWidth;
cloneInputForAnimation.BeginStoryboard(myStoryY);
cloneInputForAnimation.BeginStoryboard(myStoryX);
cloneInputForAnimation.BeginStoryboard(myStoryHeight);
cloneInputForAnimation.BeginStoryboard(myStoryWidth);
C# CloneXaml:
public static T CloneXaml<T>(T source)
{
string xaml = XamlWriter.Save(source);
StringReader sr = new StringReader(xaml);
XmlReader xr = XmlReader.Create(sr);
return (T)XamlReader.Load(xr);
}
Sorry but these are my first steps with WPF so the code will probably have a lot of problems.
Okay, as I imagined I was using a completely wrong approach.
This is the working version I have arrived at.
XAML:
<TextBox x:Name="nInput" MaxLength="17" PreviewTextInput="numberValidationTextBox" Grid.Column="0"
Height="80" Width="240"
Foreground="White" Background="#202020"
HorizontalAlignment="Left" BorderThickness="0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
FontSize="25">
<TextBox.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="12" />
</Style>
</TextBox.Resources>
</TextBox>
C#:
Duration animDuration = new Duration(new TimeSpan(0, 0 ,0, 0,800));
//I clone the input box to perform the animation
TextBox cloneInputForAnimation = CloneXaml(nInput);
UserInteraction.Children.Add(cloneInputForAnimation);
//Removing text from the realInput
nInput.Text = "";
//nInput.IsEnabled = false;
//Creating a new object wich is the one that will remain after the animation
TextBox targetInput = CloneXaml(cloneInputForAnimation);
targetInput.Visibility = Visibility.Hidden;
targetInput.Width = double.NaN;
targetInput.Height = 50;
nList.Children.Add(targetInput);
//Obtaining the destination point
Point targetPosition = targetInput.TransformToAncestor(rootView).Transform(new Point(0, 0));
//Obtaining the current point
Point currentPos = cloneInputForAnimation.TransformToAncestor(rootView).Transform(new Point(0, 0));
//Calculating the translation to do to reach targetPosition
Vector translateToDo = targetPosition - currentPos;
TranslateTransform myTranslateTransform = new TranslateTransform();
cloneInputForAnimation.RenderTransform = myTranslateTransform;
DoubleAnimation myAnimX = new DoubleAnimation {
Duration = animDuration,
To = translateToDo.X
};
DoubleAnimation myAnimY = new DoubleAnimation {
Duration = animDuration,
To = translateToDo.Y
};
DoubleAnimation myAnimHeight = new DoubleAnimation {
Duration = animDuration,
To = 50,
};
DoubleAnimation myAnimWidth = new DoubleAnimation {
Duration = animDuration,
To = 50,
};
myTranslateTransform.BeginAnimation(TranslateTransform.YProperty, myAnimY);
myTranslateTransform.BeginAnimation(TranslateTransform.XProperty, myAnimX);
cloneInputForAnimation.BeginAnimation(TextBox.WidthProperty, myAnimWidth);
cloneInputForAnimation.BeginAnimation(TextBox.HeightProperty, myAnimHeight);
C# CloneXaml has not been modified

How can I programmatically change the border of a rectangle in WPF (storyboard animation)?

In order to change a color of a rectangle in code behind I can use:
InitializeComponent();
StackPanel myStackPanel = new StackPanel();
myStackPanel.Margin = new Thickness(20);
Rectangle myRectangle = new Rectangle();
myRectangle.Name = "MyRectangle";
// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());
this.RegisterName(myRectangle.Name, myRectangle);
myRectangle.Width = 100;
myRectangle.Height = 100;
SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
this.RegisterName("MySolidColorBrush", mySolidColorBrush);
myRectangle.Fill = mySolidColorBrush;
SolidColorBrush blackBrush = new SolidColorBrush();
blackBrush.Color = Colors.Black;
myRectangle.StrokeThickness = 4;
myRectangle.Stroke = blackBrush;
ColorAnimation myColorAnimation = new ColorAnimation();
myColorAnimation.From = Colors.Blue;
myColorAnimation.To = Colors.Red;
myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
Storyboard.SetTargetProperty(myColorAnimation,
new PropertyPath(SolidColorBrush.ColorProperty));
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myColorAnimation);
myRectangle.MouseEnter += delegate(object sender, MouseEventArgs e)
{
myStoryboard.Begin(this);
};
myStackPanel.Children.Add(myRectangle);
this.Content = myStackPanel;
How can I do the same for the border of the rectangle (actually I'm looking more for a blinking-border effect than the fading one, maybe by changing Stroke property)?
Modify your Stroke:
SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
this.RegisterName("MySolidColorBrush", mySolidColorBrush);
myRectangle.Fill = mySolidColorBrush;
SolidColorBrush blackBrush = new SolidColorBrush(Colors.Black);
this.RegisterName("MySolidColorBorderBrush", blackBrush);
myRectangle.StrokeThickness = 4;
myRectangle.Stroke = blackBrush;
And storyboard setup
ColorAnimation myColorAnimation = new ColorAnimation();
myColorAnimation.From = Colors.Black;
myColorAnimation.To = Colors.Blue;
myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myColorAnimation, "MySolidColorBorderBrush");
Storyboard.SetTargetProperty(myColorAnimation,
new PropertyPath(SolidColorBrush.ColorProperty));
Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myColorAnimation);
Just for the archive (xaml)
<StackPanel Margin="20">
<Rectangle x:Name="MyRectangle" Width="100" Height="100" StrokeThickness="4">
<Rectangle.Fill>
<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
</Rectangle.Fill>
<Rectangle.Stroke>
<SolidColorBrush x:Name="MySolidColorBorderBrush" Color="Black" />
</Rectangle.Stroke>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard Duration="0:0:1">
<ColorAnimation Storyboard.TargetName="MySolidColorBrush" Storyboard.TargetProperty="Color" From="Blue" To="Red" />
<ColorAnimation Storyboard.TargetName="MySolidColorBorderBrush" Storyboard.TargetProperty="Color" From="Black" To="Blue" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</StackPanel>

Flipped copy of a Rectangle using ScaleTransform in WPF

I am trying to create a flipped copy of a rectangle in Left, Right, Up and Down directions.
However I am able to do it when the selected rectangle has no prior transformation applied on it.
But once a rectangle is being transformed with ScaleTransform and I try to flip it again in the desired direction, the solution does not work for me.
Please correct me what I am doing wrong here.
Here is the code I am using
XAML
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Canvas Name="canvas" Background="LightGray" MouseDown="Canvas_MouseDown">
<Rectangle x:Name="rect" StrokeThickness="2" Stroke="Black" Height="60" Width="100" Canvas.Left="500" Canvas.Top="300" MouseDown="rect_MouseDown">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FFFB7D0E" Offset="0" />
<GradientStop Color="#FF59C103" Offset="1" />
<GradientStop Color="#FFFFFFFF" Offset="0.51" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Canvas>
<StackPanel Grid.Column="2">
<Button Content="Left" Click="Button_Click"></Button>
<Button Content="Right" Click="Button_Click"></Button>
<Button Content="Up" Click="Button_Click"></Button>
<Button Content="Down" Click="Button_Click"></Button>
</StackPanel>
</Grid>
C#
Rectangle selectedRect;
//Flip
private void Button_Click(object sender, RoutedEventArgs e)
{
if (selectedRect == null)
return;
//Create a copy of rectangle
string rectXaml = XamlWriter.Save(selectedRect);
StringReader stringReader = new StringReader(rectXaml);
XmlReader xmlReader = XmlReader.Create(stringReader);
Rectangle newRect = (Rectangle)XamlReader.Load(xmlReader);
//Calculate the bounding box
var boundingRect = newRect.RenderTransform.TransformBounds(new Rect(0, 0, newRect.Width, newRect.Height));
double cX, cY, sX, sY;
sX = 1;
sY = 1;
cX = (boundingRect.Left + boundingRect.Right) / 2;
cY = (boundingRect.Top + boundingRect.Bottom) / 2;
switch ((sender as Button).Content.ToString())
{
case "Up":
sX = 1;
sY = -1;
cY = boundingRect.Top;
break;
case "Down":
sX = 1;
sY = -1;
cY = boundingRect.Bottom;
break;
case "Left":
sX = -1;
sY = 1;
cX = boundingRect.Left;
break;
case "Right":
sX = -1;
sY = 1;
cX = boundingRect.Right;
break;
}
newRect.Stroke = Brushes.Black;
newRect.RenderTransform = new ScaleTransform(sX, sY, cX, cY);
newRect.MouseDown += new MouseButtonEventHandler(rect_MouseDown);
//Add new rect to Canvas
canvas.Children.Add(newRect);
}
//To select a rectangle on canvas
private void rect_MouseDown(object sender, MouseButtonEventArgs e)
{
if (selectedRect != null)
selectedRect.Stroke = Brushes.Black;
selectedRect = sender as Rectangle;
selectedRect.Stroke = Brushes.Blue;
e.Handled = true;
}
//clear the selection
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (selectedRect != null)
{
selectedRect.Stroke = Brushes.Black;
selectedRect = null;
}
e.Handled = true;
}
the issue in your code was that you were trying to flip again the flipped, but the transform used was not not flipped. also the canvas location were not set properly to get the desired.
I did try to rewrite your code, this approach will flip the rectangle from the center and will move via canvas location properties i.e. left & top
private void Button_Click(object sender, RoutedEventArgs e)
{
if (selectedRect == null)
return;
//Create a copy of rectangle
string rectXaml = XamlWriter.Save(selectedRect);
StringReader stringReader = new StringReader(rectXaml);
XmlReader xmlReader = XmlReader.Create(stringReader);
Rectangle newRect = (Rectangle)XamlReader.Load(xmlReader);
double cX, cY, sX, sY;
sX = 1;
sY = 1;
cX = newRect.Width / 2;
cY = newRect.Height / 2;
double top = Canvas.GetTop(selectedRect);
double left = Canvas.GetLeft(selectedRect);
switch ((sender as Button).Content.ToString())
{
case "Up":
sX = 1;
sY = -1;
top -= selectedRect.Height;
break;
case "Down":
sX = 1;
sY = -1;
top += selectedRect.Height;
break;
case "Left":
sX = -1;
sY = 1;
left -= selectedRect.Width;
break;
case "Right":
sX = -1;
sY = 1;
left += selectedRect.Width;
break;
}
Canvas.SetLeft(newRect, left);
Canvas.SetTop(newRect, top);
newRect.Stroke = Brushes.Black;
ScaleTransform trans = newRect.RenderTransform as ScaleTransform;
if (trans != null)
{
//flip the previous transform
trans.ScaleX *= sX;
trans.ScaleY *= sY;
}
else
{
//create a new if none
trans = new ScaleTransform(sX, sY, cX, cY);
}
newRect.RenderTransform = trans;
newRect.MouseDown += new MouseButtonEventHandler(rect_MouseDown);
//Add new rect to Canvas
canvas.Children.Add(newRect);
//optional to set the new rect as selected for continuous flip copy
newRect.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = Rectangle.MouseDownEvent });
}
give it a try, see if this is what you are looking for. I would suggest you to go for data binding, that will make things lot easier.

Change margin property via visual state in windows store app (XAML+C#)

I have a List of objects, which I am showing on a ListView. Now I want to change the Margin property of the ListViewItem based on a value present in ViewModel .
Now, whenever I am running the code , I am getting the following error:-
"Exception Target sp1 does not define any VisualStateGroups."
I have no idea what's I am doing wrong. Any help will be highly appreciated
Please find my code snippet as below:-
The XAML (Please focus on Expanded and Collapsed state):-
`
<UserControl.DataContext>
<vm:BasketUserControlViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
</UserControl.Resources>
<!-- Page content -->
<Grid x:Name="ContentGrid" Width="400" Height="600">
<ListView
x:Name="BasketListView"
Width="400"
Height="Auto"
ItemsSource="{Binding Nodes}"
SelectionMode="None"
IsItemClickEnabled="True">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemClick">
<core:InvokeCommandAction Command="{Binding NavigateToChild}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<ListView.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Back" HorizontalAlignment="Left" Command="{Binding NavigateToParent}" CommandParameter="{Binding Items, ElementName=BasketListView}"/>
<Button Content="Expand" HorizontalAlignment="Right" Command="{Binding ExpandBasketEntry}" CommandParameter="{Binding Items, ElementName=BasketListView}">
</Button>
</StackPanel>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="sp1" Orientation="Horizontal" Margin="20,0,10,0" Background="Black">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Test">
<VisualState x:Name="Expanded">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="sp1">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding Level, Converter={StaticResource basketMarginCnverter}}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Collapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="sp1">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock Text="{Binding Title}" HorizontalAlignment="Left"/>
<Button HorizontalAlignment="Right">
<Image Source="ms-appx:///Assets/RightArrow.jpg" Width="10" Height="10"></Image>
</Button>
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding ElementName=ContentGrid, Path=DataContext.IsExpandedBasket}" Value="True">
<core:GoToStateAction StateName="Expanded"/>
</core:DataTriggerBehavior>
<core:DataTriggerBehavior Binding="{Binding ElementName=ContentGrid, Path=DataContext.IsExpandedBasket}" Value="False">
<core:GoToStateAction StateName="Collapsed"/>
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
`
The ViewModel:-
public class BasketUserControlViewModel : ViewModel
{
private INavigationService _navigationService;
private ObservableCollection<BasketEntryModel> _nodes;
private List<BasketEntryModel> _basketEntryList;
public ICommand ExpandBasketEntry { get; private set; }
public ICommand NavigateToChild { get; private set; }
public ICommand NavigateToParent { get; private set; }
private bool _isExpandedBasket;
public bool IsExpandedBasket
{
get { return _isExpandedBasket; }
set
{
base.SetProperty(ref this._isExpandedBasket, value);
}
}
public BasketUserControlViewModel()
{
_nodes = new ObservableCollection<BasketEntryModel>();
_basketEntryList = new List<BasketEntryModel>();
BasketEntryModel parent1 = new BasketEntryModel();
parent1.Title = "Parent 1";
parent1.BasketID = 1;
parent1.Level = 1;
List<BasketEntryModel> parent1Childs = new List<BasketEntryModel>();
BasketEntryModel parent1Child1 = new BasketEntryModel();
parent1Child1.Title = "Parent 1 Child 1";
parent1Child1.BasketID = 5;
parent1Child1.Level = 2;
BasketEntryModel parent1Child2 = new BasketEntryModel();
parent1Child2.Title = "Parent 1 Child 2";
parent1Child2.BasketID = 6;
parent1Child2.Level = 2;
BasketEntryModel parent1Child3 = new BasketEntryModel();
parent1Child3.Title = "Parent 1 Child 3";
parent1Child3.BasketID = 7;
parent1Child3.Level = 2;
BasketEntryModel parent1Child4 = new BasketEntryModel();
parent1Child4.Title = "Parent 1 Child 4";
parent1Child4.BasketID = 8;
parent1Child4.Level = 2;
parent1Childs.Add(parent1Child1);
parent1Childs.Add(parent1Child2);
parent1Childs.Add(parent1Child3);
parent1Childs.Add(parent1Child4);
parent1.Childs = parent1Childs;
List<BasketEntryModel> parent1Child1Childs = new List<BasketEntryModel>();
BasketEntryModel parent1Child41 = new BasketEntryModel();
parent1Child41.Title = "Parent 1 Child 41";
parent1Child41.BasketID = 8;
parent1Child41.Level = 3;
BasketEntryModel parent1Child42 = new BasketEntryModel();
parent1Child42.Title = "Parent 1 Child 42";
parent1Child42.BasketID = 8;
parent1Child42.Level = 3;
BasketEntryModel parent1Child43 = new BasketEntryModel();
parent1Child43.Title = "Parent 1 Child 43";
parent1Child43.BasketID = 8;
parent1Child43.Level = 3;
BasketEntryModel parent1Child44 = new BasketEntryModel();
parent1Child44.Title = "Parent 1 Child 44";
parent1Child44.BasketID = 8;
parent1Child44.Level = 3;
BasketEntryModel parent1Child45 = new BasketEntryModel();
parent1Child45.Title = "Parent 1 Child 45";
parent1Child45.BasketID = 8;
parent1Child45.Level = 3;
parent1Child1Childs.Add(parent1Child41);
parent1Child1Childs.Add(parent1Child42);
parent1Child1Childs.Add(parent1Child43);
parent1Child1Childs.Add(parent1Child44);
parent1Child1Childs.Add(parent1Child45);
parent1Child4.Childs = parent1Child1Childs;
BasketEntryModel parent2 = new BasketEntryModel();
parent2.Title = "Parent 2";
parent2.BasketID = 2;
parent2.Level = 1;
BasketEntryModel parent3 = new BasketEntryModel();
parent3.Title = "Parent 3";
parent3.BasketID = 3;
parent3.Level = 1;
BasketEntryModel parent4 = new BasketEntryModel();
parent4.Title = "Parent 4";
parent4.BasketID = 4;
parent4.Level = 1;
_basketEntryList.Add(parent1);
_basketEntryList.Add(parent2);
_basketEntryList.Add(parent3);
_basketEntryList.Add(parent4);
_nodes = new ObservableCollection<BasketEntryModel>(_basketEntryList);
NavigateToChild = new DelegateCommand<object>(GetChildBasketEntryData);
NavigateToParent = new DelegateCommand<object>(GetParentBasketEntryData);
ExpandBasketEntry = new DelegateCommand<object>(ExpandBasketEntryData);
}
public BasketUserControlViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
}
public ObservableCollection<BasketEntryModel> Nodes
{
get
{
return _nodes;
}
set
{
base.SetProperty(ref this._nodes, value);
}
}
private void GetChildBasketEntryData(object parameter)
{
try
{
var basketEntry = ((ItemClickEventArgs)parameter).ClickedItem as BasketEntryModel;
Nodes = new ObservableCollection<BasketEntryModel>(basketEntry.Childs);
}
catch (Exception e)
{
Debug.WriteLine("Exception " + e.Message);
}
}
private void GetParentBasketEntryData(object parameter)
{
try
{
ItemCollection itemCollection = (ItemCollection)parameter;
BasketEntryModel basketEntry = (BasketEntryModel)itemCollection.FirstOrDefault();
if (basketEntry != null)
{
List<BasketEntryModel> _flattenList = _basketEntryList.FlatList();
var previousLevel = basketEntry.Level - 1;
if (previousLevel != 0)
{
List<BasketEntryModel> _parentList = (from o in _flattenList where o.Level == previousLevel select o).ToList();
Nodes = new ObservableCollection<BasketEntryModel>(_parentList);
}
}
}
catch (Exception e)
{
Debug.WriteLine("Exception " + e.Message);
}
}
private void ExpandBasketEntryData(object parameter)
{
try
{
ItemCollection itemCollection = (ItemCollection)parameter;
List<BasketEntryModel> _currentBasketEntries = itemCollection.Cast<BasketEntryModel>().ToList<BasketEntryModel>();
if (_currentBasketEntries != null && _currentBasketEntries.Count > 0)
{
List<BasketEntryModel> _flattenList = _currentBasketEntries.FlatList();
if (_flattenList != null && _flattenList.Count > 0)
{
Nodes = new ObservableCollection<BasketEntryModel>(_flattenList);
IsExpandedBasket = true;
}
}
}
catch (Exception e)
{
Debug.WriteLine("Exception " + e.Message);
}
}
}
Thank and Regards,
Sam

How do you get Canvas.SetTop to work on ItemsCollection items?

Below is the complete program (XAML and code behind). When started you will see:
When you click [Show/Hide] you see:
When you uncheck Home and Documents and click [Apply] you see:
As you can see, the Home and Documents folder disappear, but the other folders do not move up. Looking at the two list boxes you see three items per line, folder label, the value for Canvas.SetTop, and True: Visibility.Visible or False: Visibility.Collapsed. The Original Placement list box are the original values. Each time you click [Apply] the New Placement List box shows that new values. The Visibility value gets applied and the result is visible, either the folder is there or it is not. However, applying the Canvas.SetTop does not work. And you can see that the values have changed for the Canvas.SetTop. And, if you look at the code, you see that it is applied to the same UIElement as the Visibility is applied to:
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work and
iec.Visibility = f.FolderVisible; // this does work?
I need the folders that are visible to move up to take the place of those that are collapsed. Canvas.SetTop is not working. How can I get the Folders to move?
Here is the Solution Explorer pane so that you know what is needed:
Here is the XAML:
<Window x:Class="FolderTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:FolderTest"
Title="Folder Test"
Width="450"
Height="410">
<Grid Margin="10">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<!-- Folder Show/Hide Button -->
<Button Name="FolderOptions" Content="Show/Hide" FontSize="8" FontWeight="Bold" Width="50" Height="20" Margin="116, 0, 0, 0" Click="Event_ShowHide_ButtonClick" />
<!-- Folders -->
<Grid>
<Canvas Name="xFolders"
Width="170"
Height="309"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0,10,0,0">
<Canvas.Resources>
<local:Folders x:Key="myFolders" />
</Canvas.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource myFolders}}">
<ItemsControl.Template>
<ControlTemplate>
<ItemsPresenter />
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
</DataTemplate.Resources>
<Canvas Name="FolderCanvas"
Tag="{Binding Path=Number}"
Visibility="{Binding Path=FolderVisible}">
<Path Data="{Binding Path=FolderPath}"
Stroke="{Binding Path=Brush}"
Fill="{Binding Path=Brush}"/>
<StackPanel Orientation="Horizontal"
Canvas.Left="5"
Canvas.Top="2">
<TextBlock Text="{Binding Path=Label}"
FontSize="12"
Margin="20, 0, 0, 0" />
</StackPanel>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top"
Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="10, 0, 0, 0">
<TextBlock Text="Original Placemet:" />
<ListBox Name="OriginalPlacement" />
<Button Name="Refresh" Content="New Placemet:" Margin="0, 10, 0, 0" Click="Event_OriginalPlacement_ButtonClick" />
<ListBox Name="NewPlacement" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
Here is the C# Code Behind:
using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using System.Collections.ObjectModel;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace FolderTest
{
public struct GD
{
public static MainWindow MainWindow = null;
public static Button Refresh = null;
}
public partial class MainWindow : Window
{
public MainWindow()
{
GD.MainWindow = this;
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
GD.Refresh = Refresh;
Folders.Placement(ref OriginalPlacement);
}
private void Event_ShowHide_ButtonClick(object sender, RoutedEventArgs e)
{
var pu = Folders.FolderShowPopUp();
pu.PlacementTarget = FolderOptions;
pu.IsOpen = true;
}
private void Event_OriginalPlacement_ButtonClick(object sender, RoutedEventArgs e)
{
Folders.Placement(ref NewPlacement);
}
}
public class Folder
{// Specification for each folder tab seen on the left.
public int Number { get; set; } // Folder sequence number top to bottom starging with 1.
public string Label { get; set; } // The label that appears on the folder tab itself.
public double Top { get; set; } // Position of the upper left corner of this folder.
public SolidColorBrush Brush { get; set; } // Solid Color Brush.
public string FolderPath { get; set; } // Contains the geometric path to draw this folder and its tab.
public Visibility FolderVisible { get; set; } // This folder's Show/Hide flag.
public Folder(int Number, string Label, double Top, SolidColorBrush Brush, string FolderPath, Visibility FolderVisible)
{
this.Number = Number;
this.Label = Label;
this.Top = Top;
this.Brush = Brush;
this.FolderPath = FolderPath;
this.FolderVisible = FolderVisible;
}
}
public class Folders : ObservableCollection<Folder>
{
public static ObservableCollection<Folder> Me = null;
private static string[] Labels = new string[]
{
"Personal",
"Health",
"Finances",
"Home",
"Employment",
"Insurance",
"Documents",
"Contacts",
"Journal"
};
private static Dictionary<string, Tuple<bool, bool>> LabelData = new Dictionary<string, Tuple<bool, bool>>()
{// Label Show Hidable
{Labels[0], new Tuple<bool, bool>(true, false)},
{Labels[1], new Tuple<bool, bool>(true, true)},
{Labels[2], new Tuple<bool, bool>(true, true)},
{Labels[3], new Tuple<bool, bool>(true, true)},
{Labels[4], new Tuple<bool, bool>(true, true)},
{Labels[5], new Tuple<bool, bool>(true, true)},
{Labels[6], new Tuple<bool, bool>(true, true)},
{Labels[7], new Tuple<bool, bool>(true, false)},
{Labels[8], new Tuple<bool, bool>(true, true)}
};
private static string[] FolderColors = new string[]
{
"FF36579E",
"FFDF2024",
"FF16A146",
"FF00B2D4",
"FFF47B20",
"FF9F1F63",
"FF13A89E",
"FFB7B7E7",
"FF50CAF5",
"FFAA9E74",
"FF86787D",
"FF36D146",
};
private static byte[] ARGBColor = new byte[4]; // Byte array for the folder top color.
private static void ColorString2ARGB(ref byte[] bytes, string Hex)
{// Converts 8 char hex string to 4 byte array.
for (int i = 0; i < 8; i += 2)
bytes[i / 2] = Convert.ToByte(Hex.Substring(i, 2), 16);
}
private static int colorIndex = -1; // Initial value of the colorIndex.
private static string NextColor()
{// Returns a 8 char string containing the next top FolderColor. If at end, cycle to beginning.
colorIndex++;
if (colorIndex >= FolderColors.Length) colorIndex = 0;
return FolderColors[colorIndex];
}
private class FolderShow
{
public string Label { get; set; }
public bool Show { get; set; }
public bool Hidable { get; set; }
public FolderShow(string label, bool show, bool hidable)
{
Label = label;
Show = show;
Hidable = hidable;
}
}
private static List<FolderShow> FolderShowList = null;
public static Popup FolderShowPopUp()
{
var pu = new Popup()
{
Placement = PlacementMode.Right,
AllowsTransparency = true,
StaysOpen = false
};
var b = new Border()
{
BorderThickness = new Thickness(2),
BorderBrush = new SolidColorBrush() { Color = Colors.Black },
CornerRadius = new CornerRadius(5),
Background = new SolidColorBrush() { Color = Colors.White }
};
var sp = new StackPanel()
{
Orientation = Orientation.Vertical,
Margin = new Thickness(10)
};
var tb = new TextBlock()
{
Text = "Checked Folders will be Displayed: ",
FontSize = 16,
FontWeight = FontWeights.Bold,
};
sp.Children.Add(tb);
foreach (var fs in FolderShowList)
{
var cb = new CheckBox()
{
Content = fs.Label,
IsChecked = fs.Show,
IsEnabled = fs.Hidable,
FontSize = 14,
FontWeight = FontWeights.Bold,
Margin = new Thickness(0, 5, 0, 0)
};
cb.Click += new RoutedEventHandler(Event_CheckBoxFolderList_Click);
sp.Children.Add(cb);
}
var bp = new StackPanel()
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 5, 0, 0)
};
var ba = new Button() { Content = "Apply", Width = 50, Height = 25, BorderBrush = new SolidColorBrush(Colors.Transparent), Tag = pu };
ba.Click += new RoutedEventHandler(Event_ApplyFolderList_Click);
bp.Children.Add(ba);
var bc = new Button() { Content = "Cancel", Width = 50, Height = 25, BorderBrush = new SolidColorBrush(Colors.Transparent), Tag = pu, Margin = new Thickness(7, 0, 0, 0) };
bc.Click += new RoutedEventHandler(Event_CancelFolderList_Click);
bp.Children.Add(bc);
sp.Children.Add(bp);
var tbm = new TextBlock()
{
Text = "Disabled folders cannot be hidden.",
Margin = new Thickness(0, 5, 0, 0),
FontSize = 12,
Foreground = new SolidColorBrush() { Color = Colors.Red }
};
sp.Children.Add(tbm);
b.Child = sp;
pu.Child = b;
return pu;
}
private static void Event_CheckBoxFolderList_Click(object sender, RoutedEventArgs e)
{
var cb = (CheckBox)e.Source;
foreach (var fs in FolderShowList)
{
if (fs.Label == cb.Content as string)
{
fs.Show = (bool)cb.IsChecked;
break;
}
}
}
private static void Event_CancelFolderList_Click(object sender, RoutedEventArgs e)
{
((Popup)((Button)e.Source).Tag).IsOpen = false;
}
private static void Event_ApplyFolderList_Click(object sender, RoutedEventArgs e)
{
foreach (var fs in FolderShowList)
{
var sh = LabelData[fs.Label];
LabelData[fs.Label] = new Tuple<bool, bool>(fs.Show, sh.Item2);
}
((Popup)((Button)e.Source).Tag).IsOpen = false;
int p = 0;
foreach (var f in Me)
{
var fs = LabelData[f.Label].Item1 == true;
f.Top = p * folderPositionFactor;
f.FolderVisible = fs ? Visibility.Visible : Visibility.Collapsed;
if (fs) p += 1;
}
foreach (ItemsControl ic in GD.MainWindow.xFolders.Children)
{
for (int i = 0; i < ic.Items.Count; i++)
{
var ie = (UIElement)ic.ItemContainerGenerator.ContainerFromIndex(i);
var iec = Controls.FindChildByType<Canvas>(ie, "FolderCanvas");
if (iec != null)
{
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work and
iec.Visibility = f.FolderVisible; // this does work?
}
}
break;
}
GD.Refresh.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
public static void Placement(ref ListBox lb)
{
lb.Items.Clear();
foreach (ItemsControl ic in GD.MainWindow.xFolders.Children)
{
for (int i = 0; i < ic.Items.Count; i++)
{
var ie = (UIElement)ic.ItemContainerGenerator.ContainerFromIndex(i);
var iec = Controls.FindChildByType<Canvas>(ie, "FolderCanvas");
if (iec != null)
{
var f = Me[i];
lb.Items.Add(f.Label + ": " + f.Top.ToString() + ", " + (f.FolderVisible == Visibility.Visible ? "True" : "False"));
}
}
break;
}
}
public Folders()
{// The constructor, initializes itself by creating a folder for each label in Labels
FolderShowList = new List<FolderShow>();
for (int i = 0; i < Labels.Length; i++)
{
string n = Labels[i];
bool h = LabelData[Labels[i]].Item2;
bool s = h == true ? LabelData[Labels[1]].Item1 == true : true;
FolderShowList.Add(new FolderShow(n, s, h));
}
SetFolders();
Me = this;
}
const double folderPositionFactor = 31; // 21.80;
public void SetFolders()
{
SolidColorBrush[] scb = new SolidColorBrush[Labels.Length]; // Hold the linear solid color brush to assign to the folder.
Color[] pointerColor = new Color[Labels.Length]; // Hold the color to assign to the folder's solid color brush.
for (int i = 0; i < Labels.Length; i++)
{// Create a solid color brush for each folder.
ColorString2ARGB(ref ARGBColor, NextColor()); // Color into byte array.
Color TmpColor = Color.FromArgb(ARGBColor[0], ARGBColor[1], ARGBColor[2], ARGBColor[3]); // Create top color.
pointerColor[i] = TmpColor;
SolidColorBrush TmpSCB = new SolidColorBrush() { Color = TmpColor };
scb[i] = TmpSCB; // Assign the solid color brush.
}
// All is ready to create the individual folders.
const string folderHeight = "56"; // "44";
Brush FontColor = Brushes.Black; // Initial font color for labels.
string fp = "M0,7 A7,7 90 0 1 7,0 L100,0 105,18 150,18 150,FH 0,FH Z".Replace("FH", folderHeight); // Initial geometric path for folder design.
int afp = 0; // Actual Folder Position.
for (int i = 0; i < Labels.Length; i++)
{// Create the individual folders.
bool fs = FolderShowList[i].Show;
Add(new Folder(
i + 1, // Folder sequence count.
Labels[i], // Folder label.
afp * folderPositionFactor, // Position of top of folder.
scb[i % scb.Length], // Solid color brush.
fp, // Geometric path for folder design.
fs ? Visibility.Visible : Visibility.Collapsed // User Hidden.
)
);
if (fs) afp += 1;
if (i == 0)
{// First folder created, now set values for remaining folders.
FontColor = Brushes.White;
fp = "M0,25 A7,7 90 0 1 7,18 L13,18 18,0 100,0 105,18 150,18 150,FH 0,FH Z".Replace("FH", folderHeight);
}
}
}
}
public static class Controls
{
public static T FindChildByType<T>(DependencyObject parent, string childName) where T : DependencyObject
{
if (parent == null) return null; // No parent, get out of here.
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChildByType<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
}
}
Thanks for any help that you can provide.
I finally figured out the answer, and it is very simple.
This is the code to be changed (the center line only):
var f = Me[i];
Canvas.SetTop(iec, f.Top); // Why does this not work
iec.Visibility = f.FolderVisible; // and this does work?
This is the new code (only the center line is changed, actually, only one character is removed):
var f = Me[i];
Canvas.SetTop(ie, f.Top);
iec.Visibility = f.FolderVisible;
That is, change Canvas.SetTop(iec, f.Top); to Canvas.SetTop(ie, f.Top); that is, only change iec to ie and all works fine.

Categories

Resources