Get ScrollViewer.ViewPortHeight in XAML - wpf

All,
I'm sure this is easy to do, but I'm still trying to do this. I can get the ViewPortHeight of a scrollviewer in code-behind and plug into loaded and size-changed events to manage objects related to ViewPortHeight, but I was wondering if there is a simpler way to do this without going to CodeBehind at all.
i.e. what I would like to do is somehow get ViewPortHeight (and ViewPortWidth) in XAML and Bind them either directly to another element's size property or to my ViewModel (with my own IValueconverter, of course). This way, I hope to more easily manage related controls whenever a window is resized (or whenever a scaletransform is done).
i.e. in PseudoCode, this is what I hope to accomplish (without CodeBehind)
(assuminging MyConverter is an IValueConverter in resources)
<ScrollViewer Name="MyScrollViewer">
<loc:MyControl Width="{Binding Path=ViewPortWidth, ElementName=MyScrollViewer, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource MyConverter}, ConverterParameter=10}" />
</ScrollViewer>
Thanks in advance.

"to manage objects related to ViewportWidth" - that sounds to me like you need a layout container such as Grid which allows you to create rows and columns. Inside those you can place whatever UIElement you wish. You can also tell a cell to have fix size or to use relative values based on avialable space.
If you want to figure out ViewportWidth you will need to do this:
<ScrollViewer Name="MyScrollViewer">
<StackPanel>
<Button Width="100"/>
<Button Width="200"/>
<Button Width="300"/>
<Button Width="400"/>
</StackPanel>
</ScrollViewer>
<TextBlock Text={Binding ElementName=MyScrollViewer, Path=ViewportWidth}"/>
ScollViewer has the property called ViewportWidth. See http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer.aspx

Should be:
<ScrollViewer Name="MyScrollViewer">
<loc:MyControl Width="{Binding Path=ViewportWidth, ElementName=MyScrollViewer, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource MyConverter}, ConverterParameter=10}" />
<!--Notice that it is ViewportWith, not ViewPortWidth-->
</ScrollViewer>

Related

How to get value of ActualHeight, pass it into a converter which feeds the result into ActualWidth

newbie to xaml here. I have a question for you all.
I have a UserControl called "ResourceMonitor" and I am trying to make sure that when its surrounding task bar gets scaled (already written and tested), that the ActualWidth of my UserControl gets scaled 1 and a half times the ActualHeight (as to keep things consistent when scaling).
I am trying to use RelativeSource to retrieve the size of ActualHeight (of the ResourceMonitor UserControl), and then pass it as a parameter to my converter, and then assign it to width. Is there a better/easier way to do this?
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,2,0">
<rm:ResourceMonitor Margin="0,0,4,0"
ShowMemoryTrace="True"
MemoryTraceColor="#FF3399FF"
Width="{Binding Path = {Path=ActualHeight, RelativeSource={RelativeSource Self}}, Converter={StaticResource ResourceMonitorWidthScalingConverter}}">
</rm:ResourceMonitor>
<Viewbox>
<StackPanel Orientation="Horizontal">
<local:ServerStatus DataContext="{Binding ServerStatusViewModel}"/>
</StackPanel>
</Viewbox>
</StackPanel>
I am getting a couple errors here, one is saying "Type path is used like a markup extension but does not derive from MarkupExtension"
and
"The arguement name is missing"
Please help :/
Your binding expression is written wrongly.
Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}},
Converter={StaticResource ResourceMonitorWidthScalingConverter}}"
Using converter is one of the recommended way to do it for MVVM. However, if you don't mind code-behind, you can do it in code-behind by handling events. But seriously, using converter is much easier.

Move a popup after databinding

I would like to display a popup above my control when the user 'mouse over', problem is that the contents of the popup is a ListBox that databinds so I do not know the size of the control (to set the margins), I have tried every event listed in intellisense on the popup but the ActualHeight of the popup is zero (want to subtract from Margin.Top), any ideas?
For this, I would recommend using a ToolTip. This way you can format the tool tip to show with a list box that is data bound. For example: A normal tooltip would look like:
<sdk:Label Content="{Binding SomeBinding}" Width="Auto" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ToolTipService.ToolTip>
<ToolTip Content="Some tool tip." Name="ttSomeToolTip"></ToolTip>
</ToolTipService.ToolTip>
</sdk:Label>
What I assume you're doing is this:
<sdk:Label Content="{Binding SomeToolTip}" Width="Auto" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ToolTipService.ToolTip>
<ToolTip Name="ttSomeToolTip">
<ListBox ItemsSource="{Binding MyListBoxSource}" SelectedItem="{Binding MySelectedListBoxItem,Mode=TwoWay}">
</ListBox>
</ToolTip>
</ToolTipService.ToolTip>
</sdk:Label>
I can't guarantee that this proposed solution will work, but you have one of two options: Target the LayoutUpdated event for the ToolTip and do a render transform matrix to resize the tool tip container. Theoretically, the tool tip should resize automatically. So if you're not already using the above mentioned approach, try that and see what happens.
You must run code via Dispatcher, then your code runs in UI thread and you have access to control size.
See here

Binding OnwayToSource not working as expected - what alternatives?

Ive a business object - call it Fish (not derived from anything ie not a DependancyObject) that is displayed in a ListBox using a DataTemplate. Else where in code I need to know the rendered width of the TextBlock part of the Fish DataTemplate through a reference to a Fish. No problem I thought. I added a width and height properties to Fish class and in my data template I bound the TextBlock width/height to these using Mode=OnwayToSource.
Problem: the Width/Height are always NaN when setting my Fish.width/heigh properties. I tried this workaround:
OneWayToSource Binding seems broken in .NET 4.0
but it doesnt work either (value is always NaN).
I cant bind to ActualWidth/ActualHeight because they are read only (why can't I bind OnwayToSource on a readonly property!!)
What alternatives do I have? Do I have to derive Fish from DependancyObject and make my properties DPs?
XAML:
<DataTemplate DataType="{x:Type p:Fish}">
<Border BorderBrush="Black" BorderThickness="2" >
<TextBlock FontSize="14" TextAlignment="Center" VerticalAlignment="Center"
Width="{Binding Path=width, Mode=OneWayToSource}"
Height="{Binding Path=height, Mode=OneWayToSource}" ...
Code:
class Fish {
public double width { get; set; } // From DataTemplate TextBlock.Width.
public double height { get; set; } // From DataTemplate TextBlock.Height
}
...
double rendered_width = my_fish.width; // Use the rendered width!
I've finally realized what you're trying to do, and you're right that it should work. WPF, however, disagrees. I see that it's a problem that others have had before, but that is apparently by design. You can't set up a binding on a read only property, even if you're just wanting to bind OneWayToSource.
Here is a question with the same problem: OneWayToSource binding from readonly property in XAML Their workaround was to put a container (which has read/write width/height) around the xaml element and set up the binding on that container. This might work for you.
There is an unresolved issue related to this on Microsoft Connect where it is claimed to be behaviour by design: http://connect.microsoft.com/VisualStudio/feedback/details/540833/onewaytosource-binding-from-a-readonly-dependency-property. Someone claims a workaround in the related thread which uses a converter. You can try it, but I'm not sure it'll work in your case, as their binding was to a custom control, not a built in framework element.
Even Better
In This Solution, Boogaart came up with an implementation defining a new attached property (Similar to DockPanel.Dock="Top") which allows any element to provide its width and height for observation:
<TextBlock ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"
Try it on and see if it fits.
If you consume these properties after some sort of action i.e. a button press or click on a hyperlink, then you can pass in the the ActualWidth and Height via a CommandParameter of a command. Otherwise I would suggest using triggers such as the ones available here:
http://expressionblend.codeplex.com/wikipage?title=Behaviors%20and%20Effects&referringTitle=Documentation
I agree that it appears counter intuitive that OneWayToSource bindings don't work on read only dependency properties.
Try binding OneWay. I think OneWayToSource is means wants to write to the source.
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx
I did a test and sure enough Width = NaN until width is Assigned (set). I understand this is not the behavior you want. Try this. Where the Width is assigned it is reported (as 200). Where the Width is not assigned it is reported as NaN. But ActualWidth IS correct. ActualWidth is there but clearly the way you are trying to get it is not working.
<StackPanel Orientation="Vertical">
<Border BorderThickness="1" BorderBrush="Red">
<TextBlock Name="tbwidthA" Text="{Binding Path=Howdy}" HorizontalAlignment="Left" Width="200"/>
</Border>
<TextBlock Name="tbwidthAw" Text="{Binding ElementName=tbwidthA, Path=Width}" HorizontalAlignment="Left"/>
<TextBlock Name="tbwidthAaw" Text="{Binding ElementName=tbwidthA, Path=ActualWidth}" HorizontalAlignment="Left" />
<Border BorderThickness="1" BorderBrush="Red">
<TextBlock Name="tbwidthB" Text="{Binding Path=Howdy}" HorizontalAlignment="Left" />
</Border>
<TextBlock Name="tbwidthBw" Text="{Binding ElementName=tbwidthB, Path=Width}" HorizontalAlignment="Left" />
<TextBlock Name="tbwidthAbw" Text="{Binding ElementName=tbwidthB, Path=ActualWidth}" HorizontalAlignment="Left" />
<Button Content="TBwidth" Click="Button_Click_1" Width="60" HorizontalAlignment="Left" />
</StackPanel>
What is interesting is the Button does report the correct ActualWidth but Width is NaN.
Debug.WriteLine(tbwidthB.Width.ToString());
Debug.WriteLine(tbwidthB.ActualWidth.ToString());

Is it possible to bind to a control's property from a datatemplate?

Ok, sounds odd, and there's likely a better way, but I haven't seen it yet.
What I'm trying to do is restyle a ListPicker under Windows Phone 7.
What I need is to
get rid of the header (that's easy, just define a null ListPicker.HeaderTemplate).
Force the picker to always go to full mode when clicked (again, easy, just set the ItemCountThreshold to 1).
Restyle the itemtemplate used when in FullMode (again, easy, just define a FullModeItemTemplate)
Incorporate the ListPicker's "HEADER" property value into the ItemTemplate (since only one item will ever show, i need the header text "embedded" within the one item).
It's that number 4 that I can't seem to get.
I've defined a listpicker like so (i'm directly defining the templates inline instead of in resources for now, just to keep things simple).
<phonekit:ListPicker Header="Header Text" x:Name="ListOfSounds"
SelectedItem="{Binding Path=DepartureChime, Mode=TwoWay, Converter={StaticResource EnumDescriptionToStringConverter}}"
ItemCountThreshold="1">
<phonekit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Text="{TemplateBinding Header}" />
<TextBlock Text="{Binding}" />
<TextBlock Text=">" />
</StackPanel>
</DataTemplate>
</phonekit:ListPicker.ItemTemplate>
Ignoring all the obvious formatting bits for now, the problem I'm having is that I can't use {TemplateBinding Header} from within a datatemplate. I've used it from a ControlTemplate no problem.
The result of this ItemTemplate should be an item displayed such as
{TextOfHeader}{Content of selected Item}>
I'm just not sure how to go about getting at a property of the templated control (the listpicker in this case).
Any ideas?
Take advantages of RelativeSource:
<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType={x:Type phonekit:ListPicker}}}" />

What Does this MSDN Sample Code Do? - ItemsControl.ItemTemplate

This is a XAML code sample taken from the MSDN library article for the ItemsControl.ItemTemplate property:
<ListBox Width="400" Margin="10" ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm looking for an explanation of the usage of the <StackPanel> element is this example.
->
Where is this panel going to exist in the ListBox?
What is its purpose in the ItemTemplate?
Can any System.Windows.Controls.Panel be used in its place, specifically a Grid?
How would I go about using a <Grid> element as the template for each item in the ListBox?
Here is the concept I am going for:
http://img151.imageshack.us/img151/7960/graphconcept.png
I have drawn the graph using a <Path> element, and there are no problems there.
I am working on the labels for the axies, and I am experimenting with the use of a <Grid> element in the ItemTemplate - but I have no idea how the grid is supposed to function in this context, and MSDN says nothing about the panel in their sample code.
My XAML for the Y-axis labels currently looks like this:
<ListBox Background="Transparent" BorderThickness="0" ItemsSource="{Binding Path=GraphLabelYData}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding Path=GraphLabelSpacing}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="{Binding ElementName=GraphLabelYData, Path=GraphLabelMarkerLength}" />
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="{Binding Path=GraphLabelTag}" />
<Rectangle Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Stroke="Black" Fill="Black" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Does this look correct? Nothing shows up at run-time, but I want to make sure the XAML is modeled correctly before I start debugging the data-bindings and the code-behind.
"Where is this panel going to exist in the ListBox?" - The listbox will make one copy of it for each list item, i.e. one for each element in the myTodoList collection. So within each list item, you'll have the three labels stacked one above the other.
"What is its purpose in the ItemTemplate?" - To make it possible to show more than one control for each element in the ItemsSource. ItemTemplate, like many things in WPF, can only take one child element, so if you want multiple children, you need to specify how you want them laid out, and you do that by adding a panel (StackPanel in this case).
"Can any System.Windows.Controls.Panel be used in its place, specifically a Grid?" - You bet.
"How would I go about using a <Grid> element as the template for each item in the ListBox?" - The same way you would use a Grid anywhere else. It's no different; it's just that ItemsControl (and its descendant, ListBox) will create multiple instances of your Grid. Note, though, that inside the ItemTemplate, your DataContext will be the current list item, and therefore your {Binding}s will be relative to that list item (unless you specify otherwise with e.g. ElementName).
"Does this look correct?" - This really should be posted as a separate question, as it's unrelated to the questions about the MSDN sample, and I'm not even sure what you're trying to do. But I'll try to answer: I suspect something is wrong, because you're using the name "GraphLabelYData" two different ways. In the ColumnDefinition, as far as I can tell, you're treating GraphLabelYData as the name of a XAML element (i.e. you're looking for another control in the window/page/UserControl with Name="GraphLabelYData" or x:Name="GraphLabelYData", and reading that control's GraphLabelMarkerLength property); but in the TextBlock, you're treating GraphLabelYData as the name of a property on the current collection item. I suspect one of those isn't right.

Resources