Binding OnwayToSource not working as expected - what alternatives? - wpf

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());

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.

Get ScrollViewer.ViewPortHeight in XAML

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>

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}}}" />

WPF XAML Binding

Hello
I have a problem with a binding that I want to do and can't find any information on how to do it.
Basically I want to bind an object to a property of another object.
For example
<TextBox Text="test" Tag="{Binding ElementName=TxtBx2}" x:Name="TxtBx1"/>
<TextBox Text="test" x:Name="TxtBx2"/>
This is kind of weird but it would help on the code that i'm implementing, so on a property of one object I want to have another object bind in xaml.
I don't know if this is possible, any pointers would be helpful
Thanks, Ruben
That is how you do it; you just need to specify the Path.
<TextBox Text="test" Tag="{Binding ElementName=TxtBx2, Path=Text}" x:Name="TxtBx1"/>
<TextBox Text="test" x:Name="TxtBx2"/>
If you are wanting the DataContext of the TextBox; then your Path would change accordingly.
<TextBox Text="test" Tag="{Binding ElementName=TxtBx2, Path=DataContext}" x:Name="TxtBx1"/>
<TextBox Text="test" x:Name="TxtBx2"/>
If you are needing to use the Tag property within a WPF application you might want to re-evaluate your approach as I have yet to use the Tag property since moving from WinForms as that need has been replaced by leveraging the data binding functionality within WPF.
UPDATE:
If your goal is to bind to a given control versus a property on the control; then don't specify the property name within the Path.
Based on your goal; attached behaviors would be a better approach and allow you to wrap the functionality within the extended DataGrid.
<TextBox Text="test" Tag="{Binding ElementName=TxtBx2,Path=Text}" x:Name="TxtBx1"/>
<TextBox Text="test" x:Name="TxtBx2"/>
Assuming you want the value of the Text property of TxtBx1 to be the value of the Text property in TxtBx2, you would use:
<TextBox x:Name="TxtBx1" Text="{Binding ElementName=TxtBx2, Path=Text}" />
<TextBox x:Name="TxtBx2" Text="test" />
Update
Assuming (possibly incorrectly again!) that you want to bind the TxtBx1 element to the Tag of TxtBx2, you would use:
<TextBox x:Name="TxtBx1" Text="test" />
<TextBox x:Name="TxtBx2" Tag="{Binding ElementName=TxtBx1}" Text="test" />
Just out of interest, why do you want to do such a thing?
Update 2
Assuming that you have a Datagrid that you've extended from the wpftoolkit datagrid and a user control that is a pager for that Datagrid, and when you move to another page you need to do some processing on the datagrid, then why don't you just either update the datagrid in your page change event (if using code behind), or update the items that the datagrid is bound to in your page change verb on your view model (if using MVVM)?
Well, it does make sense to bind to an entire object(not to any specific property) with items control such as this:
<ListBox x:Name="pictureBox"
ItemsSource=”{Binding Source={StaticResource photos}}" …>
......
</ListBox>

WPF TextBlock Binding to DependencyProperty

I have what I believe to be about one of the most simple cases of attempting to bind a view to a dependencyproperty in the view model. It seems that the initial changes are reflected in the view but other changes to the DP do not update the view's TextBlock. I'm probably just missing something simple but I just can't see what it is. Please take a look...
My XAML has a status bar on the bottom of the window. I want to bind to the DP "VRAStatus".
<StatusBar x:Name="sbar" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2"
VerticalAlignment="Bottom" Background="LightBlue" Opacity="0.4" DockPanel.Dock="Bottom" >
<StatusBarItem>
<TextBlock x:Name="statusBar" Text="{Binding VRAStatus}" />
</StatusBarItem>
<StatusBarItem>
<Separator Style="{StaticResource StatusBarSeparatorStyle}"/>
</StatusBarItem>
</StatusBar>
My viewmodel has the DP defined:
public string VRAStatus
{
get { return (string)GetValue(VRAStatusProperty); }
set { SetValue(VRAStatusProperty, value); }
}
// Using a DependencyProperty as the backing store for VRAStatus.
public static readonly DependencyProperty VRAStatusProperty =
DependencyProperty.Register("VRAStatus", typeof(string), typeof(PenskeRouteAssistViewModel),new PropertyMetadata(string.Empty));
Then, in my code I set the DP:
VRAStatus = "Test Message...";
Is there something obvious here that I am missing? In my constructor for the viewmodel I set the DP like this:
VRAStatus = "Ready";
I never get the Test Message to display.
You need to add DataContext="{Binding RelativeSource={RelativeSource Self}} in .
As it turns out things were a little more complicated than I had thought (like, when is that NOT the case :) My RibbonControl was in a UserControl to get all of that XAML out of the MainWindow. It was the fact that it was in a UserControl that made it work differently with the ViewModel. I don't know why - probably one of those mysteries that won't ever be solved. But by putting my RibbonControl directly on the MainWindow, everything works as expected - both with a DP and a C# Property. Interesting. (Wish I could get back those two days of my life!)
thanks,
Bill
Try specifying the name of the DP with the Path flag in the binding like this:
<TextBlock x:Name="statusBar" Text="{Binding Path=VRAStatus}">
Bill,
When and where do you set the DataContext? I had problems in the past that when I set the DataContext before the InitializeComponent, my Bindings never executed properly.
Also, for curiosity's sake: why do you use a DP in your ViewModel instead of just a Property?
Try to specify the UpdateSourceTrigger property of the Binding:
<StatusBar x:Name="sbar" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2"
VerticalAlignment="Bottom" Background="LightBlue" Opacity="0.4" DockPanel.Dock="Bottom" >
<StatusBarItem>
<TextBlock x:Name="statusBar" Text="{Binding VRAStatus, UpdateSourceTrigger=PropertyChanged}" />
</StatusBarItem>
<StatusBarItem>
<Separator Style="{StaticResource StatusBarSeparatorStyle}"/>
</StatusBarItem>
</StatusBar>

Resources