StackOverflowException on InitializeComponent - c#

I'm trying to follow this little tutorial, but I keep getting this exception.
The relevant XAML looks like this:
<StatusBar Margin="0,288,0,0" Name="statusBar" Height="23" VerticalAlignment="Bottom">
<StatusBar.DataContext>
<m:MainWindow />
</StatusBar.DataContext>
<TextBlock Name="statusText" Text="{Binding Path=StatusBarText, NotifyOnTargetUpdated=True}" DataContext="{Binding}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</StatusBar>
I'm guessing I'm getting the StackOverflowException because I'm trying to use MainWindow as the DataContext. I want to use the MainWindow because it seems like a logical place to put my StatusBarText property,
public partial class MainWindow : Window
{
public string StatusBarText { get; set; }
It makes it easier to access in my code-behind event handlers.
What am I supposed to do then? Where am I supposed to put this property? Or is there a way to set the DataContext to "this" so that it doesn't create a new instance of MainWindow and just refers to itself?

I generally set my DataContext in code-behind, in the constructor (I usually use MVVM, but have used a window in small temp projects):
public MainWindow()
{
statusBar.DataContext = this;
}
Note that in your shown code example, you will only get your initial StatusBarText value, because you are not implementing INotifyPropertyChanged.

Ideally your properties which you will bind to should live within a ViewModel following the MVVM pattern, abstracting themselves away from the View. Since that is not your question however we shall move along...the DataContext is inherited from its parent. Therefore if the StatusBar lives in the Window, which I am pretty certain it does, it will already be inheriting the DataContext from the Window. You are essentially trying to bind a UI component using a UI component (Window) as your source for the DataContext. Far from ideal...here is an overview of the MVVM pattern...

Related

Custom Control Storyboard Template Binding Not Working - XAML C#

I created a custom Control which has some Dependency Properties which values I also Need to assign to the storyboard in the ControlTemplate.
Here is the beginning of the Code of my Control Template:
<ControlTemplate TargetType="local:RingPresenter">
<Grid x:Name="RootGrid">
<Grid.Resources>
<Storyboard x:Key="StatisticUpdateAnnimation">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(RingSlice.EndAngle)" Storyboard.TargetName="ringSlice">
<EasingDoubleKeyFrame KeyTime="0" Value="45"/>
<EasingDoubleKeyFrame KeyTime="0:0:2.2" Value="{TemplateBinding Angle}">
The Important part is the Value={TemplateBinding Angle}" - the value is getting applied succesfully to my
<helper:RingSlice InnerRadius="100" Radius="150" StartAngle="0" EndAngle="{TemplateBinding Angle}" Fill="DarkCyan" x:Name="ringSlice">
</helper:RingSlice>
... which is part of my Control Template, but in the storyboard the value is staying 0. The Debugger is saying that Anglehas the correct value and it is working fine for my Ringslice and even the metadata should be 45.
Why is this value not applied to my storyboard? How can I fix this?
When in doubt, swap EndAngle="{TemplateBinding Angle}" for EndAngle="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Angle}".
TemplateBinding is quick and useful but doesn't always have the strongest capacity to feed through information it's bound to, for reasons that escape me entirely.

Datatrigger Not Firing Storyboard Or Not At All

I can not seem to get Data triggers to work no matter what I do. I'm trying to play a storyboard from the view model when a value changes yet nothing happens, no error, no cant find binding, just nothing... The code I'm currently attempting to get working is:
XAML:
<Page
DataContext="{Binding ViewModel, Source={StaticResource Locator}}">
<Page.Resources>
<DataTrigger x:Key="alertInDataTrigger" Binding="{Binding alert}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource alertIn}" x:Name="alertIn_start"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="alertIn_start" />
</DataTrigger.ExitActions>
</DataTrigger>
<Storyboard x:Key="alertIn" Changed="visible" >
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="alert">
<EasingThicknessKeyFrame KeyTime="0:0:0.3" Value="729,2,2,658"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
<\Page.Resources>
C#:
public int alert
{
get { return this._alert; }
set
{
if (this._alert != value)
{
int oldalert = this._alert;
this._alert = value;
RaisePropertyChanged("alert");
}
}
}
I can see the int alert properly changing to 1 and I know the pages bindings are working as all the other bindings on the page are properly showing their bindings yet I can not seem to get data triggers to work. I could use a little help on this stumper :/
I see two things you are doing wrong. One is that triggers are should be added to the Page.Triggers not Page.Resources. The trigger will never fire if it is in the resources.
The other is Storyboard.TargetName="alert". This should not be set to alert unless you have an element in the Page that is named alert. I believe that if you don't add Story.TargetName then it defaults to the current element which is the page. Otherwise, set the name of the element you want to animate its margin.

How do I make a Block Arrow blink?

I have a block arrow and i want to make it blink by just filling it with green. I would like to be able to stop it also. I have a right click menu to start it and stop it.
This is what i have so far. But i cant figure out how to start it. I tried to access it but i got an error:
All objects added to an IDictionary must have a
Key attribute or some other type of key associated with them. Line 11 Position 10.
Here is my xaml code:
<ed:BlockArrow x:Name="ArrowLeft" Fill="Green" HorizontalAlignment="Left" Height="29" Margin="142,0,0,-3" Orientation="Left" Stroke="#FF13FF00" VerticalAlignment="Bottom" Width="39" />
<Window.Resources>
<Storyboard x:Name="Blink" AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ArrowLeft"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01" Value="Green"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
So, in the right click menu to start it i have:
private void MenuItemLeft_Click(object sender, RoutedEventArgs e)
{
Storyboard blinkAnimation = TryFindResource("Blink") as Storyboard;
if (blinkAnimation != null)
{
blinkAnimation.Begin();
}
Is there a better way to do this? or what am i doing wrong?
WPF Resources are dictrionaries, hence everything within a Resource must have a key. You can add a key by adding an x:Key attribute. You can then locate your item by indexing into the Resource dictionary directly, Resources["MyKeyName"]
Regarding your method of implementation, it looks fine to me.

Architecting...a slideshow

I know this sounds silly and I could use some out-of-the-box solution, but I really want to build my own simple image slideshow. I've been doing application development in Silverlight/WPF for some time, but for whatever reason I can't wrap my head around this.
I have an observable collection of SlideshowItem
Each SlideshowItem has Source which indicates where the image for it is located
I show a translucent box for each SlideshowItem (horizontal list using a stackpanel) and when you click, you should transition to that slide
So here's my problem: If I have that list with a stackpanel template, and under the list is an image taking up the size of the canvas, I can bind the context of the image to the selected SlideshowItem. That's all well and good. But when I click/change the selected index of the list, I want to do a crossfade or slide between two images.
How should I represent this in Silverlight? Should I actually have a scroll panel or something with all the images and then change between them? Or is it sufficient to use a single image control? Can I do this with states, or do I need to explicitly run a storyboard? Any samples would be appreciated.
You can use the TransitioningContentControl from the Silverlight Toolkit, however if you want to roll your own you will need two content controls and swap out the "Active" one on SelectionChanged events. You also can fire your storyboards here.
ContentControl _active;
private void LB_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_active == Content1)
{
_active = Content2;
Content2Active.Begin();
} else
{
_active = Content1;
Content1Active.Begin();
}
_active.Content = LB.SelectedItem;
}
And the Xaml looks something like this. I just use strings and text, but this approach should work reasonable well for images too:
<Grid x:Name="LayoutRoot" Background="White" MaxHeight="200">
<Grid.Resources>
<Storyboard x:Name="Content1Active">
<DoubleAnimation From="0" To="1" Storyboard.TargetName="Content1" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation To="0" Storyboard.TargetName="Content2" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
<Storyboard x:Name="Content2Active">
<DoubleAnimation From="0" To="1" Storyboard.TargetName="Content2" Storyboard.TargetProperty="(UIElement.Opacity)" />
<DoubleAnimation To="0" Storyboard.TargetName="Content1" Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</Grid.Resources>
<StackPanel>
<ListBox x:Name="LB" SelectionChanged="LB_SelectionChanged" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>Red</sys:String>
<sys:String>Green</sys:String>
<sys:String>Blue</sys:String>
</ListBox>
<Grid>
<ContentControl x:Name="Content1" FontSize="40" Foreground="{Binding Content, RelativeSource={RelativeSource Self}}">
</ContentControl>
<ContentControl x:Name="Content2" FontSize="40" Foreground="{Binding Content, RelativeSource={RelativeSource Self}}">
</ContentControl>
</Grid>
</StackPanel>
</Grid>
Definitely you don't need the entire Image collection displayed in a scrollviewer/stackpanel. You can implement this in many different ways. I can explain a simple idea of Using one Image : As you said , define a SelectedSlide property in your ViewModel and bind that to an Image control ( Preferably a ContentControl with Image as its part of the ContentTemplate, so that you can have descriptions and other items in the same). This solution can give you the opportunity to add some storyboards so that if you increase your SelectedIndex(Another VM property) fire a storyboard to do a 'Left Move' animation and if you decrease do a 'Right Move' animation makes user feels like slides are coming from one side and going the other way. You can do pretty good UX on that set of storyboards.
Update (Idea 2) : Yes if we need the notion of the previous one leaving the view when new one coming in, we can architect it by using two ContentControls wrapped inside a CustomControl ( lets call it as SlideShowControl). SlideShowControl will have its mechanism to properly set DataContext of the two ContentControl based on the selectedIndex position. I have successfully made this control in one of my projects, the logic here is to switch the ContentControls through a storyboard so that we can have many different effects by swapping the storyboard. Suppose you move from Index 1 to 2, ContentControlA will animate to left, and B will come in to the View, and based on your next click ControlA will go sit either left or right of the View, and comes with new DataContext of the selected View.

Stopping a programmatically instantiated storyboard

I have an EventTrigger that will stop a few storyboards that are defined in my XAML ... but now i need to stop a storyboard that I'm starting programmatically.
<UserControl.Resources>
<Storyboard x:Key="FadeIn"> ... Fade In Definition </StoryBoard>
<Storyboard x:Key="FadeOut"> ... Fade In Definition </StoryBoard>
</UserControl>
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard="{StaticResource FadeIn}" x:Name="FadeIn_BeginStoryboard" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<StopStoryboard BeginStoryboardName="FadeIn_BeginStoryboard"/>
<StopStoryboard BeginStoryboardName="FadeOut_BeginStoryboard"/>
</EventTrigger>
</UserControl.Triggers>
Storyboard FadeOutStoryboard;
public void StopFadeOut() {
FadeIn_BeginStoryboard.Storyboard.Stop();
FadeOut_BeginStoryboard.Storyboard.Stop();
FadeOutStoryboard = (Storyboard) FindResource("FadeOut");
FadeOutStoryboard.Name="FadeOutStoryboard";
FadeOutStoryboard.Begin();
}
When I put a < StopStoryboard BeginStoryboardName="FadeOutStoryboard" /> it tells me it can't find FadeOutStoryboard. I'm fairly new to WPF programming, so there might be a better way to do this and I'm open to that. The StopFadeOut() method is being invoked by a parent that creates an instance of my usercontrol.
Move these lines to the constructor. You only need to run them once.
FadeOutStoryboard = (Storyboard)FindResource("FadeOut");
FadeOutStoryboard.Name = "FadeOutStoryboard";
Then add this line right after them.
RegisterName("FadeOutStoryboard", FadeOutStoryboard);

Categories

Resources