WPF Binding to an attached property from a DataTemplate [modified]
-
Is there a good way to bind an element created through a DataTemplate to an attached property? Please refer to this post[^] for full source code (scroll down to around the smiley)... Elements are created through a DataTemplate and end up as children of a Grid. The data has Row and Col properties that need to be bound to the Grids Row and Column attached properties. The most obvious binding (to me) would be something like this:
<DataTemplate DataType="{x:Type local:Obs}"> <Button `Grid.Row="{Binding Path=Row}" Grid.Column="{Binding Path=Col}"` Click="Button\_Click"> <Button.Style> <Style TargetType="{x:Type Button}" > <Setter Property="Content" Value="{Binding Path=Name}"/> <Setter Property="Width" Value="50"/> <Setter Property="Height" Value="Auto"/> <Setter Property="Margin" Value="2"/> <Setter Property="Background" Value="#FFBBBB00"/> <Setter Property="Foreground" Value="#FF000000"/> <Style.Triggers> <DataTrigger Binding="{Binding Path=Status}" Value="On"> <Setter Property="Background" Value="#FFFFFF00"/> <Setter Property="Foreground" Value="#FFFF0000"/> </DataTrigger> </Style.Triggers> </Style> </Button.Style> </Button> </DataTemplate>
but it doesn't work, I assume because the element isn't yet a child of the grid at the time the binding is created. Based on that assumption, I figured I needed an event of some kind to give me a chance to do the binding once the created element is a child of the
-
Is there a good way to bind an element created through a DataTemplate to an attached property? Please refer to this post[^] for full source code (scroll down to around the smiley)... Elements are created through a DataTemplate and end up as children of a Grid. The data has Row and Col properties that need to be bound to the Grids Row and Column attached properties. The most obvious binding (to me) would be something like this:
<DataTemplate DataType="{x:Type local:Obs}"> <Button `Grid.Row="{Binding Path=Row}" Grid.Column="{Binding Path=Col}"` Click="Button\_Click"> <Button.Style> <Style TargetType="{x:Type Button}" > <Setter Property="Content" Value="{Binding Path=Name}"/> <Setter Property="Width" Value="50"/> <Setter Property="Height" Value="Auto"/> <Setter Property="Margin" Value="2"/> <Setter Property="Background" Value="#FFBBBB00"/> <Setter Property="Foreground" Value="#FF000000"/> <Style.Triggers> <DataTrigger Binding="{Binding Path=Status}" Value="On"> <Setter Property="Background" Value="#FFFFFF00"/> <Setter Property="Foreground" Value="#FFFF0000"/> </DataTrigger> </Style.Triggers> </Style> </Button.Style> </Button> </DataTemplate>
but it doesn't work, I assume because the element isn't yet a child of the grid at the time the binding is created. Based on that assumption, I figured I needed an event of some kind to give me a chance to do the binding once the created element is a child of the
Inside a datatemplate you can bind to a relative source, a parent, of the object by using the RelativeSource attribute. You should try and experiment with something like:
<DataTemplate DataType="{x:Type local:Obs}">
<Button Grid.Row="{Binding Path=Row, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
Grid.Column="{Binding Path=Col, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
Click="Button_Click">
..Good Luck!
Dawn is nature's way of telling you to go to bed.
-
Inside a datatemplate you can bind to a relative source, a parent, of the object by using the RelativeSource attribute. You should try and experiment with something like:
<DataTemplate DataType="{x:Type local:Obs}">
<Button Grid.Row="{Binding Path=Row, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
Grid.Column="{Binding Path=Col, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}"
Click="Button_Click">
..Good Luck!
Dawn is nature's way of telling you to go to bed.
I've experimented for HOURS on this :) Anyway, that doesn't work in the context of the code I linked to (a DataTemplate for an ItemsControl ItemsTemplate)...tried it already, and tried it again. I do appreciate the help, thanks! Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Is there a good way to bind an element created through a DataTemplate to an attached property? Please refer to this post[^] for full source code (scroll down to around the smiley)... Elements are created through a DataTemplate and end up as children of a Grid. The data has Row and Col properties that need to be bound to the Grids Row and Column attached properties. The most obvious binding (to me) would be something like this:
<DataTemplate DataType="{x:Type local:Obs}"> <Button `Grid.Row="{Binding Path=Row}" Grid.Column="{Binding Path=Col}"` Click="Button\_Click"> <Button.Style> <Style TargetType="{x:Type Button}" > <Setter Property="Content" Value="{Binding Path=Name}"/> <Setter Property="Width" Value="50"/> <Setter Property="Height" Value="Auto"/> <Setter Property="Margin" Value="2"/> <Setter Property="Background" Value="#FFBBBB00"/> <Setter Property="Foreground" Value="#FF000000"/> <Style.Triggers> <DataTrigger Binding="{Binding Path=Status}" Value="On"> <Setter Property="Background" Value="#FFFFFF00"/> <Setter Property="Foreground" Value="#FFFF0000"/> </DataTrigger> </Style.Triggers> </Style> </Button.Style> </Button> </DataTemplate>
but it doesn't work, I assume because the element isn't yet a child of the grid at the time the binding is created. Based on that assumption, I figured I needed an event of some kind to give me a chance to do the binding once the created element is a child of the
The reason why this does not work is because the ItemsControl wraps your object in a container. To get the binding you want you will need to make an StyleSelector to assign to the ItemContainerStyleSelector property of the Items control. Something like this should work:
Public Overrides Function SelectStyle(ByVal item As Object,
ByVal container As System.Windows.DependencyObject)
As System.Windows.Style
Dim matElem As Obs = TryCast(item, Obs)Dim ret As New Style() If matElem IsNot Nothing Then ret.Setters.Add(New Setter(Grid.RowProperty, matElem.PositionY)) ret.Setters.Add(New Setter(Grid.ColumnProperty, matElem.PositionX)) End If Return ret
End Function
-
I've experimented for HOURS on this :) Anyway, that doesn't work in the context of the code I linked to (a DataTemplate for an ItemsControl ItemsTemplate)...tried it already, and tried it again. I do appreciate the help, thanks! Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
My fault... I misread your post, I guess I was working a bit too late... :zzz: Anyway, I just recreated a solution based on your code in a previous post. I do not entirely understand what you're trying to do... When I click the Test button, button 3 moves to the first column and a 6th button is added to the grid. Like this:Screenshot[^]. Is this what is supposed to happen? If not, could you explain a bit more about what should happen.
Dawn is nature's way of telling you to go to bed.
-
My fault... I misread your post, I guess I was working a bit too late... :zzz: Anyway, I just recreated a solution based on your code in a previous post. I do not entirely understand what you're trying to do... When I click the Test button, button 3 moves to the first column and a 6th button is added to the grid. Like this:Screenshot[^]. Is this what is supposed to happen? If not, could you explain a bit more about what should happen.
Dawn is nature's way of telling you to go to bed.
Timmy Kokke wrote:
Is this what is supposed to happen?
Yes :) I was demonstrating to the OP how changing the data would reflect in the UI - the test showed you can change the cell position of existing objects in the collection and add items to the collection. The code I posted works like I wanted it to....I just didn't like the way I did the binding from the data object's Row and Col properties to the Grid.Row and Grid.Column attached properties, and I could't figure out a better way, so I started this new thread. Cheers, Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
modified on Saturday, November 15, 2008 12:51 PM
-
The reason why this does not work is because the ItemsControl wraps your object in a container. To get the binding you want you will need to make an StyleSelector to assign to the ItemContainerStyleSelector property of the Items control. Something like this should work:
Public Overrides Function SelectStyle(ByVal item As Object,
ByVal container As System.Windows.DependencyObject)
As System.Windows.Style
Dim matElem As Obs = TryCast(item, Obs)Dim ret As New Style() If matElem IsNot Nothing Then ret.Setters.Add(New Setter(Grid.RowProperty, matElem.PositionY)) ret.Setters.Add(New Setter(Grid.ColumnProperty, matElem.PositionX)) End If Return ret
End Function
Excellent Gideon, thank you! StyleSelector is news to me :) I actually needed bindings, not just setters for the initial value, but I'm much more comfortable setting them in the item style selector than in the grid ArrangeOverride like I was doing in my hacked solution. This is a much more appropriate place to set the bindings and I was able to remove the silly IsBoundToUI flag from the data object class. Here's what I did:
public class ObsItemStyleSelector : StyleSelector { public override Style SelectStyle(object item, DependencyObject container) { Obs obs = item as Obs; ContentPresenter cp = container as ContentPresenter; if (null != obs && null != cp) { Binding binding = new Binding("Row"); binding.Source = obs; cp.SetBinding(Grid.RowProperty, binding); binding = new Binding("Col"); binding.Source = obs; cp.SetBinding(Grid.ColumnProperty, binding); } return null; } }
That works well! Thanks again, Mark
Mark Salsbery Microsoft MVP - Visual C++ :java: