Debugging resource value problems in XAML and vb? - wpf

I have what seems like a straightforward problem: make an application which can handle any resolution display.
Background:
This is a display-only application (no user interface). It is for display of information, and I've made a test app to work out an issue. Have spent a few days trying to find a solution to this one tiny problem. Either need a clever programming solution (which has thus far eluded me) or a different way of approaching the problem (again, has been elusive).
This is a WPF application with VB.net code behind. The composition of the one and only window needs to maintain the same relative layout to the display components, and the application is intended to run full-screen (this demo runs in a window, so I can resize it easily and test different "screen layouts").
All but one part of the layout works fine.
The part with the problem is a "bar" which is intended to animate up or down, depending on circumstances. The size of the bar already changes appropriately, depending on the size of the application window. However... the distance the bar travels is dependent on the size of that bar; the shorter it is, the fewer pixels it has to travel, and conversely, a higher resolution screen necessitates it travel further. Animation is accomplished by changing the height of a grid object which contains both a rectangle (the bar) and a viewport which in turn has a textblock object.
I've created a resource value in XAML with a value which represents the default height of the grid object which contains the bar. If that resource is bound to the value of the keyframe as a static resource, the value is passed to storyboard and the animation happens. However, I have not been able to change the value in this configuration. When the size of the window changes, VB code attempts to change the resource key value, but no joy. Have also tried this with the binding done as a dynamic resource.
Lots of reading shows that some bindings into XAML work, while others (such as keyframes) aren't so lucky; something about "being freezable," which seems silly, as this shouldn't be that hard. Other things I've read suggest that the "fix" is to implement solution in the code behind. Most don't provide any other info on how that might be done, and the few other items suggestion methods which seem absurdly circuitous.
Here is the XAML, with some inline comments to point out what's going on:
<Window x:Name="SmartClock" x:Class="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:WpfApplication3"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<!-- Set a variable (resource) to be used as the height value in animation -->
<System:Double x:Key="OnAirBarUp">84</System:Double>
<!-- Simple keyframe animation, which slides a bar with text into view -->
<Storyboard x:Key="story_OnAirUp" x:Name="OnAirUp">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<!-- ****** HERE'S WHERE THE "FUN" BEGINS :/
This line needs to have a target value which changes, depending on the
height of objects in the window, and those are affected by the size
of the window. The intention is to make this a full-screen app, but
can be used on many different monitors, so the resolution is not known
at design time.
The resource I set earlier in the XAML ("84") will be used for this
keyframe value, and the animation works... but is only "correct" for
the default window size.
(yes, I know that the bar starts in the "up" position currently, but that
is just to help me see it as I debug this... when the button is pushed,
the bar should instantly disappear, and then slide back on to the screen
to the same position).
*************************************************************************** -->
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="{StaticResource ResourceKey=OnAirBarUp}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<!-- Animation is triggered by clicking the button -->
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource story_OnAirUp}"/>
</EventTrigger>
</Window.Triggers>
<!-- Here we setup the page -->
<Grid x:Name="grid_Page" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="4*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<!-- This is "status" grid at the top of the page -->
<Grid x:Name="grid_Top" Grid.Row="0" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Name="row_ProgramName" Height="1*"/>
<RowDefinition Name="row_Status" Height="1*"/>
</Grid.RowDefinitions>
<Viewbox x:Name="viewbox_ProgramName" Grid.Row=" 0" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_ProgramName" Text="Some text on top" Background="#FFFFFF12"/>
</Viewbox>
<!-- This creates a grid which will hold the "ON AIR" bar -->
<Grid x:Name="grid_OnAir" Grid.Row="1" VerticalAlignment="Bottom" ShowGridLines="True" Height="84" >
<Rectangle x:Name="rectangle_OnAir" Fill="#FF90A436" RadiusX="20" RadiusY="20" Margin="10,0" Height="83"/>
<Viewbox x:Name="viewbox_OnAir" Grid.Row="1" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_OnAir" Text="ON AIR" FontSize="64" VerticalAlignment="Top" FontWeight="Bold" Background="#FFDC0000" Margin="0" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Grid>
<!-- This is a big ugly button for triggering the storyboard -->
<Button x:Name="button1" Content="Button" Margin="0" Grid.Row="1" FontSize="26.667" HorizontalAlignment="Left" Width="257"/>
</Grid>
</Window>
And here is the VB.net code which also has copious comments:
Class MainWindow
Private Sub MainWindow_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
' Set the size of items on the screen based on the size of the window.
' "rectangle_OnAir" is the rectangle (the "On-Air" bar for the display)
' "viewbox_OnAir" allows the textblock to properly scale.
' "grid_OnAir" is the container which will be made taller and shorter
' through animating the "height" property. This allows the contents
' to slide up and down in their region of the screen.
' Since the window can be any size, we adjust the height of the
' controls to accomodate.
rectangle_OnAir.Height = grid_Top.ActualHeight / 2
viewbox_OnAir.Height = grid_Top.ActualHeight / 2
grid_OnAir.Height = grid_Top.ActualHeight / 2
' Set the height of the dynamic resource "OnAirBarUp" to a value
' which equals the height of "rectangle_OnAir".
' This will be used in the animation to allow the whole rectangle to
' be displayed.
Resources("OnAirBarUp") = grid_Top.ActualHeight / 2
End Sub
End Class
This is one version of many permutations.
When the code runs without re-sizing the window, this is what it looks like:
This is the default layout of this test page... the "On Air" bar is visible, and pressing the "button" causes it to disappear for a moment, and then immediately animate upwards into the current position
With the window small, the animation causes the bar to overshoot the location where it need to be (the grid height value is too large for this size window
Making the window larger than default has the opposite effect on the animation; the bar undershoots the landing location.
I've contemplated wrapping the whole display in a viewbox control, so the whole mess can scale to fit whatever display, and tried some of this, with less-than-stellar results. It was overly complicated, and there are distortions in the graphics and text.
From what troubleshooting I could do, it seems that the resource value does change, but not the value at the binding point; I'm pretty much stuck with whatever value was used at the moment the application was launched.
It seems odd to provide a framework for dynamically-configurable layout, and for extensive animation capabilities, and then have it hamstrung by those two aspects not being able to "play nice" together because a simple double value can't be passed from the code to the layout markup.
Please excuse the length of the post; I'm trying to be thorough, and trying to anticipate what someone might write in response. Also please excuse the hideous colors of the sample -- what was an aid to me during the layout of this test, not intended for the final application.
Ideally, the best solution would allow for any size display, without the program having any foreknowledge of that resolution. Also, being able to resize during program execution would be nice, in case the program can be used (after all) in a less-than-full-screen arrangement. In that case, it would need to be able to gracefully handle most reasonable window sizes (I'd have to code in some lower boundary limits to the window size).
Whatever solution I use, it would need to be able to handle other similar animation needs (other parts need to move on and off the screen, moving the correct amount for the given circumstances).

I had posted this last night on both Stack Overflow and on Microsoft's MSDN forums, and got a good reply on MSDN. That pointed me in the right direction, and I posted a follow-up message with a detailed description of what worked for me. Here is that follow-up report:
Andy,
Thanks for the quick and detailed reply!
I'll start with this part: It now works.
It did take me a couple more hours of groping (using your clues) to get there. I started this reply at that time (about four hours ago), but persistence paid off.
-------------
You say: "I get that you're animating the bar but not what it's relative to and where it's coming or going to."
I'm guessing you mean literally, as in where on the screen, as opposed to conceptually (as in purpose).
The literal "where" is to have the bar rise out of the middle third (approximately) region of the screen (divided horizontally). The whole application is a clock with some value-added features for use in a newsroom. I already have a working clock based generally on this design, but the "On Air" bar isn't implemented yet. This will be triggered by an event over a network socket connection, quite literally when the studio is "on air." When that happens, the text in the upper third of the screen is shrunk gracefully as the "on air" bar rises up, essentially halfway into the top third region.
The animation works properly if I don't resize the window. Since I don't know what size monitors will be used in the different implementations of this, and since this will end up being at some of our other facilities which do the same thing, I need to build it at this stage so it is flexible enough to handle a 1024x768 screen or a 1920x1080, or something in-between, or 4K, or whatever.
I could make the bar a fixed size, but that would go against the balance of the layout (aesthetic concern) and the readability of right-sized text (usability concern).
There are to be other status fields which will be able to move into view, depending on circumstances (other things happening in real time, triggered by events over the computer network), which will need to have similar flexibility of the positioning of the animations.
Looking over the marquee code you pointed out to me, it seems that binding is done to an object, but I'm unsure how that method could change the distance that a keyframe could move the object, just adjust the properties of the object being moved.
-------------
I looked over the "marquee" code, though it took some patience to dissect it. The C# isn't too bad, but I'm not fluent, so I turned to an online code converter to translate it to VB:
Public Partial Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private SBMarquee As Storyboard
Private XAnimation As DoubleAnimation
Private Sub Window_ContentRendered(sender As Object, e As EventArgs)
SBMarquee = TryCast(Me.Resources("SBmarquee"), Storyboard)
XAnimation = TryCast(SBMarquee.Children(0), DoubleAnimation)
XAnimation.[To] = MarqueeContainer.ActualWidth * -1
AddHandler Me.SizeChanged, AddressOf Window_SizeChanged
End Sub
Private Sub Window_SizeChanged(sender As Object, e As SizeChangedEventArgs)
XAnimation.[To] = MarqueeContainer.ActualWidth * -1
MarqueeContainer.Visibility = Visibility.Hidden
SBMarquee.Begin()
MarqueeContainer.Visibility = Visibility.Visible
End Sub
End Class
Cool.
I'm not familiar with partial classes, but a quick check online seemed to suggest to me that I could just include the relevant parts into my VB class for the main window.
I don't know if the "initializeComponent" part is required, but it didn't throw an error. However, the attempt to create a Storyboard object met with some resistance. I figured that I needed an "Imports" command to get the proper namespace added (full disclosure: the idea of "namespaces" isn't obvious to me, but I get that something needed to be imported... the "namespace" moniker doesn't seem to fit, and I'm too literal). Here is what I added, right at the very top (above the "class" line):
Imports System.Windows.Media.Animation
Now these lines works:
Private story_OnAirUp As Storyboard
Private timeline_OnAirUp As DoubleAnimationUsingKeyFrames
The first line creates a Storyboard object, and the second makes the necessary timeline object. I thought at first I would be making an "EasingDoubleKeyFrame" object, which I could have made, but the child object of the Storyboard object is not a keyframe (well, not with the XAML constructed using the DoubleAnimationUsingKeyFrames element); my initial attempts to use a keyframe met with resistance. Here is what I tried which didn't work:
Private story_OnAirUp As Storyboard
Private keyframe_OnAirUp As EasingDoubleKeyFrame
No errors at this point, so I thought I was golden. Not so fast, young padawan.
Here is the next code I added, altered from the marquee example:
Private Sub Window_ContentRendered(sender As Object, e As EventArgs)
story_OnAirUp = TryCast(Me.Resources("story_OnAirUp"), Storyboard)
keyframe_OnAirUp = TryCast(story_OnAirUp.Children(1), EasingDoubleKeyFrame)
keyframe_OnAirUp.Value = grid_Top.ActualHeight / 2
AddHandler Me.SizeChanged, AddressOf MainWindow_SizeChanged
End Sub
No dice. The "TryCast(story_OnAirUp.Children(1)" part generates an error:
"Value of type 'Timeline' cannot be converted to 'EasingDoubleKeyFrame'."
Well, that stinks.
After some more comparison of the code, I see that my XAML code differs from the marquee example because of the inclusion of this line:
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
So, the child of the Storyboard object isn't the EasingDoubleKeyFrame (well, not directly), but rather is the "DoubleAnimationUsingKeyFrames" element. However, the value I need to bind to isn't in the DoubleAnimationUsingKeyFrames element; it is in the EasingDoubleKeyFrame.
(Note: the sample code had "DoubleAnimation" the altered property as "To:" but the "EasingDoubleKeyFrame" uses the "value" property; I noticed that and adjusted accordingly.)
I tried to make a child object of the DoubleAnimationUsingKeyFrames element, but turns out "children" is a property of it. Then it dawned on me:
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
The "(1)" points to the second keyframe, which is the one where the "On Air" grid is at the "full height," which has a default value of 84 pixels, but needs to change.
After I got the "Window_ContentRendered" done, I tried running the code.
Error in the "MainWindow_SizeChanged" subroutine:
"An unhandled exception of type 'System.NullReferenceException' occurred in WpfApplication3.exe
Additional information: Object reference not set to an instance of an object."
Hmmm. Okay, the "MainWindow_SizeChanged" routine is running before the "Window_ContentRendered" routine, not allowing the objects to be instantiated before they were used.
I moved the object creation code from "Window_ContentRendered" to "MainWindow_SizeChanged," remarked our the "ContentRendered" routine, and then tried again.
It worked. Here's the code which works:
XAML
<Window x:Name="SmartClock" x:Class="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:WpfApplication3"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="800">
<Window.Resources>
<!-- Simple keyframe animation, which slides a bar with text into view -->
<Storyboard x:Key="story_OnAirUp" x:Name="OnAirUp">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="grid_OnAir">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<!-- **** This is the keyframe which has a changed value from the VB code -->
<EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="84"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<!-- Animation is triggered by clicking the button -->
<Window.Triggers>
<EventTrigger RoutedEvent="ButtonBase.Click" SourceName="button1">
<BeginStoryboard Storyboard="{StaticResource story_OnAirUp}"/>
</EventTrigger>
</Window.Triggers>
<!-- Here we setup the page -->
<Grid x:Name="grid_Page" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="4*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<!-- This is "status" grid at the top of the page -->
<Grid x:Name="grid_Top" Grid.Row="0" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition Name="row_ProgramName"/>
<RowDefinition Name="row_Status" Height="Auto"/>
</Grid.RowDefinitions>
<Viewbox x:Name="viewbox_ProgramName" Grid.Row=" 0" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_ProgramName" Text="Some text on top" Background="#FFFFFF12"/>
</Viewbox>
<!-- This creates a grid which will hold the "ON AIR" bar -->
<Grid x:Name="grid_OnAir" Grid.Row="1" VerticalAlignment="Bottom" ShowGridLines="True" Height="84" >
<Rectangle x:Name="rectangle_OnAir" Fill="#FF90A436" RadiusX="20" RadiusY="20" Margin="10,0" Height="83"/>
<Viewbox x:Name="viewbox_OnAir" Grid.Row="1" Margin="0" Stretch="Uniform">
<TextBlock x:Name="textblock_OnAir" Text="ON AIR" FontSize="64" VerticalAlignment="Top" FontWeight="Bold" Background="#FFDC0000" Margin="0" HorizontalAlignment="Center" />
</Viewbox>
</Grid>
</Grid>
<!-- This is a big ugly button for triggering the storyboard -->
<Button x:Name="button1" Content="Button" Margin="0" Grid.Row="1" FontSize="26.667" HorizontalAlignment="Left" Width="257"/>
</Grid>
</Window>
VB
Imports System.Windows.Media.Animation
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Private story_OnAirUp As Storyboard
Private timeline_OnAirUp As DoubleAnimationUsingKeyFrames
Private Sub MainWindow_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
' These next three lines were moved from the "ContentRendered" section
story_OnAirUp = TryCast(Me.Resources("story_OnAirUp"), Storyboard)
timeline_OnAirUp = TryCast(story_OnAirUp.Children(0), DoubleAnimationUsingKeyFrames)
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
rectangle_OnAir.Height = grid_Top.ActualHeight / 2
viewbox_OnAir.Height = grid_Top.ActualHeight / 2
grid_OnAir.Height = grid_Top.ActualHeight / 2
' This is the line which actually sets the value to the animation keyframe.
timeline_OnAirUp.KeyFrames(1).Value = grid_Top.ActualHeight / 2
End Sub
End Class
Thanks for pointing me in the right direction, and I hope my follow-up (admittedly long) helps someone else.

Related

WPF control alignment is wrong when dynamically added

Hi I thought I could solve this easily but it is driving me crazy.
I am using a UserControl to house a video player control based on VLC, along with play and stop buttons etc. I then place the UserControl on my main form. if the UserControl is declared in XAML it behaves normally.
I decided to rewrite the code to instantiate my UserControl dynamically, in case I need to destroy it and create another on the fly. But when I do the video moves to the top of its container instead of the middle.
The UserControl relevant section is here:
<Grid x:Name="LayoutParent" >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="12" />
</Grid.RowDefinitions>
<!-- I comment this if adding player dynamically -->
<!--<wpf:VlcPlayer Grid.Row="0" x:Name="Player" />-->
<!-- I un-comment this if adding player dynamically -->
<Grid x:Name="VideoPlayerPanel" Grid.Row="0" Margin="0" />
<StackPanel Grid.Row="1" Opacity="0.8">
...(buttons etc)
</StackPanel>
<ProgressBar ...(progressBar etc) />
</Grid>
My codebehind looks like this:
Dim Player As VlcPlayer = New VlcPlayer ' uncomment If adding the player dynamically
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Player.SetValue(Canvas.ZIndexProperty, -1)
VideoPlayerPanel.Children.Add(Player)
VolumeSlider.Value = 50
End Sub
I have tried VerticalAlignment="Center" and VerticalAlignment="Stretch" in XAML on the VideoPlayerPanel, with Center the video disappears entirely, with Stretch it still aligns to the top.
Any thoughts as to what I might do to align this centrally would be much appreciated!
When adding Player dynamiccaly you have different result, because you wrap Play in additional Grid. Try to add Player directly to first row of LayoutParent:
Player.SetValue(Grid.Row, 0)
LayoutParent.Children.Add(Player)
Thanks to all that replied.
I did some more research, I substituted in a Rectangle for the player control and it aligned perfectly. That led me to discover that the third party control was itself at fault. I had to get the source and change the VerticalAlignment directly.
Sorry for the runaround.
Remove Height="*" from first Row . * is used to occupy remaining space, so it is good to use it for the last Row.
Use fixed width and or Auto.

Skinning Control Backgrounds - Better Performance?

sorry if this question is overly simple, but I'm having a hard time figuring out how to create backgrounds to controls - in the hopes that it will improve app performance.
I have 9 different controls. All of them have a background. The backgrounds are made up of either images, other controls or both. All of those backgrounds have another background.
Think of this like Power Point with slides, slide layouts and slide masters - inherited in that order. I have 9 slides / controls.
The first 3 controls have the same "control layout" (let's call it
ControlLayout1). ControlLayout1 gets some of it's elements from ControlMaster1.
The second 3 controls also have the same control layout, but it is
different from the first. Let's call it ControlLayout2. It also
inherits from ControlMaster1.
The final set of 3 controls are different again. We can call them
ControlLayout3. But this time, they inherit from a different master - ControlMaster2.
Right now in each control I'm writing out all the XAML each time separately. I'm thinking there must be a way to not write these in each of these each item. Ideally, what I would like to create is one set of XAML that can be reused.
Here's some pseudo-XAML:
<UserControl x:Name="Control1">
<MyBackground (ControlLayout1)/>
</UserControl>
<UserControl x:Name="Control2">
<MyBackground (ControlLayout2)/>
</UserControl>
<UserControl x:Name="Control3">
<MyBackground (ControlLayout3)/>
</UserControl>
And then somewhere for ControlLayouts (I don't know, like Application.Resources or elsewhere)
<Canvas x:Name="ControlLayout1">
<MyMasterBackground (ControlMaster1)/>
</Canvas>
<Canvas x:Name="ControlLayout2">
<MyMasterBackground (ControlMaster1)/>
<TextBox Text="The Control 2">
</Canvas>
<Canvas x:Name="ControlLayout3">
<MyMasterBackground (ControlMaster2)/>
<TextBox Text="The Control 3">
</Canvas>
And then for the ControlMasters
<Canvas x:Name="ControlMaster1">
<Canvas.Background>
<ImageBrush ImageSource="/Images/image1.jpg" />
</Canvas.Background>
</Canvas>
<Canvas x:Name="ControlMaster2">
<Canvas.Background>
<ImageBrush ImageSource="/Images/image2.jpg" />
</Canvas.Background>
<TextBox Text="Control Master 1">
</Canvas>
Once defined, the ControlLayouts and ControlMasters never need to change - they are static.
Beyond just having a smaller XAP if I can put these all in one location and reuse the XAML, I'm hoping performance will be improved in my app as the ControlLayouts automatically get BitmapCached or something like that.
So first, is there a good strategy to implement the above (the ControlLayouts and Masters do not have any code-behind)? Secondly will performance be improved in loading of Control1, Control2, etc.? Finally, if they were pure usercontrols (i.e. they had some code behind), would that be better for performance?
Thanks in advance!
What you ask for is a combination of a few things:
About the Background thing: just create a dependency property (let's call it MyBackgroundDP) of type Brush in the code behind of a UserControl, and bind it to your XAML like:
<UserControl ...>
<Grid Background={"Binding MyBackgroundDP, RelativeSource={RelativeSource Mode=FindAncestor, AncestoryType=UserControl}}">
<!-- More XAML declarations -->
</Grid>
</UserControl>
To create the dependency property, you can use the built in snippet in visual studio: propdp
Simply write "propdp" and that TAB twice. Fill up the fields and it's all good.
Alright so that was easy enough, right? ;)
Now the tougher part: making so-called master pages.
Actually it's not that much different from the background thing.
Declare another dependency property, only this time of type object, or FrameworkElement (better).
Then in your XAML, you declare a kind of placeholder: ContentControl. Let's call it MyContentDP for this example:
<UserControl ...>
<Grid Background={"Binding MyBackgroundDP, RelativeSource={RelativeSource Mode=FindAncestor, AncestoryType=UserControl}}">
<ContentControl ContentTemplate="{Binding MyContentDP, RelativeSource={RelativeSource Mode=FindAncestor, AncestoryType=UserControl}}" />
</Grid>
</UserControl>
You can then fine tune whatever else you want to provide in this "master view", add a border around the Grid, put some flowers, you name it.
Once you're done, this is how you use it, assuming it was called MyUserControl
<Window ...
xmlns:local="...reference_to_your_usercontrol_dll/exe">
<Grid>
<local:MyUserControl MyBackgroundDP="Red">
<local:MyUserControl.MyContentDP>
<!-- More XAML declarations here -->
</local:MyUserControl.MyContentDP>
</local:MyUserControl>
</Grid>
</Window>
Now the performance point:
If you put all the XAML for this as a Custom control (which is DIFFERENT from a UserControl), you can then put all the XAML in your App.xaml
Why? because parsing XAML can be an intensive operation, and if you make WP7/SL parse it at runtime whenever you need it, you lose performance.
Instead, your App.xaml gets parsed at startup, then it's in memory. That's what's done in the loading of your application. You would get a performance boost, although it would be minimal for controls made of few XAML, it is still a good practice.
Hope this helps,
Bab.

How to make a windows slide out using WPF( expression Blend 4and C#)

I am trying to figure out how to connect windows so the slide out using a small button from the left or right of a main window. What I am trying to do is create a main form with 2 windows connected to it. For one of the windows when the user presses a button on the main window it makes the window seem to slide out rather than pop up. Here is where I got the idea http://www.youtube.com/watch?v=CVlSj0yr3rg&feature=related ..The user then changes a value and the main windows is updated with new information. Honestly I have finished writing all my code and got everything working in Windows Forms in visual studio 2010 (with pop up windows).But I am thinking to make a more appealing gui WPF is the way to go, plus I like learning about it. If you have any forums, tutorials or general answers that would be great.
OK, so judging from the video you really want some kind of expander that opens and not a Window. A Window is an area with border, and the standard buttons and titlebar at the top.
This can be done with a grid with two columns. One is set to Auto width, one is set to * width. In the Auto sized one you can put your expanding content, and have your always visible content in the other.
The simple way to do this:
The Xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="7"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid x:Name="ExpandoGrid"/>
<Button Content="..." Grid.Column="0" HorizontalAlignment="Right" Margin="0" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5" Width="6" Height="40" Click="Button_Click"/>
<Grid x:Name="OtherContentGrid" Grid.Column="1" HorizontalAlignment="Left" Height="100" Margin="-7,0,0,0" VerticalAlignment="Top" Width="100"/>
</Grid>
</Window>
The Code-behind
Imports System.Collections.ObjectModel
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Private Sub Button_Click(ByVal sender as Object, ByVal e as System.Windows.RoutedEventArgs)
If Me.ExpandoGrid.Width = 7 Then Me.ExpandoGrid.Width = 200 Else Me.ExpandoGrid.Width = 7
End Sub
End Class
This is by no means the complete way, or the best way. It is one of the simplest to implement though. A better way would be with a ViewModel which would handle the state of the expanded area, along with some animations to make it a smooth transition. If you want the sliding behaviour that is done in that video, animations are where it is at. If you are using Blend, then you have the right tool for animations.
Personally I would have this Windows ViewModel have a property (lets call it DrawerExpanded as Boolean) that a customized Expander would bind its IsExpanded property to. I would then create an open animation that sets the width of the content in the expander, and a close animation that sets the width to 0. Additionally, in each of these I would probably include setting the visibility and opacity to make the effect better and not weird. So lets say expand animation sets Width to 350 at .5 seconds, Visibility to visible at .5 seconds, and then opacity from 0 to 100 from .5 seconds to .7 seconds. That way the drawer slides out and the content fades quickly into view.
If you want a code example of that, you may have to give me a few mins.
I would really just take the easy/friendly route of creating Visual States in Expression Blend. There's basically just an "in state" and "out state", and an InteractionTrigger that allows the control to trigger the state change. Its awesome and extremely user friendly.
No code behind :) Hope it helps you!
As a bonus, you can easily add transition effects just like in a powerpoint. The xaml code gets pretty verbose, but working in Blend allows you to use the IDE to manage everything you add visually.
You can even use the Interaction Trigger to toggle between visibility states of the other controls, rather than writing converters, etc.

New to WPF - What Control to Use / Getting Started?

I'm a WPF n0ob and I'm struggling with selecting the appropriate control to get the layout I want.
What I'm trying to do is draw a bunch of squares (virtual post-it notes) onto the screen. Each note is going to be a decent size (~150 pixels or so) and there could be hundreds of these notes. I want the whole thing to be scrollable so that you can resize the window however you like and the whole thing should be zoomable.
I've done this and it works.
But what I've done seems awfully wrong....
In the code, I'm dynamically creating post it notes and adding them to a giant canvas. I'm manually doing the math to determine where to place each note and how big the canvas should be. I added some labels at the top and had to go back and add a 'Y Offset' value to push all the squares down. I actually generate three different canvas controls and then add each one of them to a stack panel that is inside of a ScrollViewer. I added a scroll bar and set the the stack panel to zoom in and out as you adjust the bar.
It 'works', but I feel like I'm really not using WPF the way it's meant to be used. I tried achieving the same thing with a grid, but the grid didn't seem to want to size itself appropriately.
Can someone tell me a 'better' way to achieve the same look?
Here's my Xaml code - as you can see; there isn't much to it....
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition />
</Grid.RowDefinitions>
<Slider x:Name="ZoomSlider" Minimum="0.01" Value="1" Maximum="2" Margin="0,0,0,6" />
<ScrollViewer x:Name="MyScroller" Grid.Row="1" HorizontalScrollBarVisibility="Visible" HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<StackPanel x:Name="TicketsGrid" Background="White" HorizontalAlignment="Center">
</StackPanel>
</ScrollViewer>
</Grid>
And then here is what I'm doing in code (ugly!!!)
For Each myWorkItem As WorkItem In myWorkItems
Dim newRect As New Border
newRect.Width = TicketSizeX
newRect.Height = TicketSizeY
If myWorkItem.State.ToUpper.Contains("HOLD") Then
newRect.Background = New SolidColorBrush(Colors.Purple)
Else
newRect.Background = New SolidColorBrush(Color)
End If
newRect.CornerRadius = New System.Windows.CornerRadius(5)
newRect.BorderThickness = New System.Windows.Thickness(1)
newRect.BorderBrush = New SolidColorBrush(Colors.Black)
Dim myPanel As New StackPanel
newRect.Child = myPanel
Dim lblTitle As New Label
lblTitle.Content = myWorkItem.Id
lblTitle.FontWeight = System.Windows.FontWeights.Bold
Dim lblDesc As New TextBlock
lblDesc.Text = myWorkItem.Title
lblDesc.TextWrapping = TextWrapping.Wrap
myPanel.Children.Add(lblTitle)
myPanel.Children.Add(lblDesc)
newRect.SetValue(Canvas.LeftProperty, CType(((TicketCount Mod TicketsXPerUser) * TicketStepX) + (xOffset * TicketStepX * TicketsXPerUser), Double))
newRect.SetValue(Canvas.TopProperty, CType(((Math.Floor((TicketCount / TicketsXPerUser)) * TicketStepY)) + NameLabelHeight, Double))
myCanvas.Children.Add(newRect)
TicketCount += 1
Next
MyCanvas.Width = (TicketStepX * TicketsXPerUser) * myTFS.SharedCodeTeam.Count
MyCanvas.Height = (CType(((Math.Floor((MaxTicket / TicketsXPerUser)) + 1) * TicketStepY), Double))
TicketsGrid.Children.Add(MyCanvas)
ScrollViewer with an ItemsControl inside.
Bind the ItemsSource property of the ItemsControl to an ObservableCollection<PostIt> (where PostIt is a plain old CLR object with all the info that goes on the post it).
Add a DataTemplate to the ItemsTemplate property of the ItemsControl
Add controls to the DataTemplate and bind them directly to an instance of PostIt
Add PostIt instances to the ObservableCollection<PostIt> in your code.
The ScrollViewer handles all scrolling. That's all you need.
The ItemsControl is designed to bind against a collection. For each instance in the collection, it figures out what DataTemplate to use, creates a copy of the template, sets the root's DataContext to the instance it pulled from the collection, and adds the template to itself. It does this for each instance found in the collection.
In your codebehind, all you need to do is create a bunch of PostIts and add them to the collection. No godawful construction of UI elements like you're doing. Urgh.
If you can grasp this concept, you are a step away from understanding MVVM, the Model-View-Controller pattern in WPF. Read about it, try it out. Its a very simple way of making very complex applications with complex UI but with a minimum of code (and none of that crap you're doing currently).

How to update Dynamic Resource within a Dynamic Resource?

I have a visual brush which is a group of shapes, the main colour of which is a dynamic resource itself - so the shape is for example MyShape and the Colour, MyColour which is referenced by the Shape object.
My problem is when I update the colour for this - it only happens the first time the shape is loaded (the colour needs to be set first) however as much as I change the colour it won't update the dynamic resource that uses the colour - how do I make this work?
Just need to make a dynamic resource work within another dynamic resource and have them both update when I change the colour.
I have no idea how to get this to work - I spent time creating a colour-picker for WPF only to find I cannot change the colour of this item - 1-Tier resources work where I set the brush/colour directly but not a colour within another object or 2-Tier Resource.
Edit: My problem seems to be specific to using these in a seperate Resource / Dictionary as my program needs to access this item from a class not the Window, the main example mentioned does not work when the MyColor is in a seperate Resource.
Unless I misunderstand the situation, exactly what you're talking about works pretty well. I just tried it out with this Xaml:
<Window x:Class="ConditionalTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<SolidColorBrush x:Key="MyColor" Color="Aqua" />
<VisualBrush x:Key="MyBrush">
<VisualBrush.Visual>
<Ellipse Height="50" Width="100" Fill="{DynamicResource MyColor}" />
</VisualBrush.Visual>
</VisualBrush>
</Window.Resources>
<Grid Background="{DynamicResource MyBrush}">
<Button Height="30" Width="Auto" VerticalAlignment="Center" HorizontalAlignment="Center" Content="ChangeColor" Click="Button_Click" />
</Grid>
</Window>
And then changed the color in the click handler for that button:
private void Button_Click(object sender, RoutedEventArgs e)
{
((SolidColorBrush)Resources["MyColor"]).Color = Colors.Purple;
}
And it worked like a champ.
Can you post an example of how you are attempting to change the color in the resource dictionary?
When I make a sample app and try to change the resource value it appears that the SolidColorBrush in the resource dictionary has been frozen so it can't be modified. To get around this I just set the new value to a new SolidColorBrush.

Resources