Silverlight Binding: User controls inside datagrid - silverlight

I have a DataGrid in my silverlight app that has a few columns. A couple basic columns bound with no issues. One column has a UserControl in it and the XAML is as follows:
<data:DataGridTemplateColumn Header="" CanUserSort="True" Width="107">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:StaticPageEnlistment EnlistmentName="{Binding SiteName}" Width="400" Height="150"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
So I have a public property that's a string called EnlistmentName that I have bound to the SiteName value. I use this same "{Binding SiteName}" in all my other colums with no issues, why can't the user control accept the same binding string?

At a guess you haven't implemented the EnlistmentName as a DependencyProperty. You would do that like this in your StaticPageEnlistment UserControl like this:-
public string EnlistmentName
{
get { return GetValue(EnlistmentNameProperty) as string; }
set { SetValue(EnlistmentNameProperty, value); }
}
public static readonly DependencyProperty EnlistmentNameProperty =
DependencyProperty.Register(
"EnlistmentName",
typeof(string),
typeof(StaticPageEnlistment ),
new PropertyMetadata(null));

Is EnlistmentName a DependencyProperty ? According to MSDN, the target of a binding must be a DependencyProperty.

Related

How to bind to ContentControl.Content's attached property

I have a modular WPF app - Shell, Module1, Module2, Module3.
Each module has views defined like this:
<UserControl>
<TextBox x:Name="DefaultControl"/>
<UserControl.Tag>
<Binding ElementName="DefaultControl"/>
</UserControl.Tag>
</UserControl>
Shell has the MainView, which uses WPFToolkit BusyIndicator control. MainView is defined like this:
<Window>
<xctk:BusyIndicator IsBusy="{Binding ElementName=MainRegion, Path=Content.DataContext.IsBusy}"
FocusAfterBusy="{Binding Path=Content.Tag, ElementName=MainRegion}">
<ContentControl x:Name="MainRegion">
</ContentControl>
</xctk:BusyIndicator>
</Window>
BusyIndicator.FocusAfterBusy is meant to set focus on any view's element named DefaultControl, when IsBusy becomes False.
It works fine when using Tag property for binding.
But now I want to use a simple attached property called DefaultControl (object) for that.
View becomes like:
<UserControl>
<TextBox x:Name="DefaultControl"/>
<ext:FocusExtension.DefaultControl>
<Binding ElementName="DefaultControl"/>
</ext:FocusExtension.DefaultControl>
</UserControl>
How should I change the Path in BusyIndicator.FocusAfterBusy="{Binding Path=Content.Tag, ElementName=MainRegion}", to bind to MainRegion.Content's attached property (DefaultControl)?
Attached property:
public static readonly DependencyProperty DefaultControlProperty = DependencyProperty.RegisterAttached(
"DefaultControl",
typeof(object),
typeof(FocusExtension),
new PropertyMetadata(null));
public static object GetDefaultControl(DependencyObject obj)
{
return (object)obj.GetValue(DefaultControlProperty);
}
public static void SetDefaultControl(DependencyObject obj, object value)
{
obj.SetValue(DefaultControlProperty, value);
}
Try this:
FocusAfterBusy="{Binding Path=Content.(ext:FocusExtension.DefaultControl),
ElementName=MainRegion}"

Property bound to DependencyProperty won't update despite TwoWay Binding set

I have a little problem here. I've created custom TreeView using RadTreeView. It all works nice, but I've encountered an obstacle. I've set DependencyProperty for SelectedItem in TreeView. I nest my control in View, bind property to SelectedItem in TwoWay mode, but bound property won't update, it's null all the time, despite DependencyProperty value being set.
Here's tree xaml:
<Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:sdk='http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk'
xmlns:telerik='http://schemas.telerik.com/2008/xaml/presentation' x:Name='this' >
<Grid.Resources>
<DataTemplate x:Key='ChildTemplate'>
<TextBlock Text='{Binding Path=ChildPath}' Margin='5,0' />
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key='NameTemplate' ItemsSource='{Binding ChildrenCollectionPath}' ItemTemplate='{StaticResource ChildTemplate}'>
<TextBlock Text='{Binding Path=ParentPath }' Padding='7'/>
</telerik:HierarchicalDataTemplate>
</Grid.Resources>
<telerik:RadTreeView x:Name='rtvTreeView' Padding='5' BorderThickness='0' IsEditable='False' IsLineEnabled='True' IsExpandOnDblClickEnabled='False' ItemTemplate='{StaticResource NameTemplate}' />
</Grid>
Below is way I nest the control in View:
<windows:TreeViewReuse CollectionSource="{Binding SitesCollectionWithAddress}" ParentPath="Napis" Grid.Column="0" BorderThickness="2" SelectedItemD="{Binding SelectedSide, ElementName=this, UpdateSourceTrigger=Explicit, Mode=TwoWay}" ChildPath="FullAddress" ChildrenCollectionPath="AdresyStrony" BorderBrush="Red" DoubleClickCommand="{Binding TreeViewDoubleClick}">
</windows:TreeViewReuse>
And here's Tree's code behind in parts:
public partial class TreeViewReuse : UserControl
{
static Telerik.Windows.FrameworkPropertyMetadata propertyMetaData = new Telerik.Windows.FrameworkPropertyMetadata(null,
Telerik.Windows.FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(SelectedItemChangedCallback));
public object SelectedItemD
{
get { return GetValue(SelectedItemDProperty); }
set { SetValue(SelectedItemDProperty, value); }
}
public static readonly DependencyProperty SelectedItemDProperty =
DependencyProperty.Register("SelectedItemD", typeof(object), typeof(TreeViewReuse), propertyMetaData);
public TreeViewReuse()
{
InitializeComponent();
Loaded += new RoutedEventHandler(TreeViewReuse_Loaded);
}
void treeView_SelectionChanged(object sender, Telerik.Windows.Controls.SelectionChangedEventArgs e)
{
SelectedItemD = _treeView.SelectedItem;
}
static private void SelectedItemChangedCallback(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
}
Does anyone have an idea why property bound to SelectedItemD does not update? I don't care about setting tree's selected item from it, I only want to set it to selected item.
Here's property:
public StronaSprawy SelectedSide
{
get
{
return _selectedSide;
}
set
{
_selectedSide = value;
}
}
Your Dependency Property looks fine.. all except for that Telerik.Windows.FrameworkPropertyMetadata instance.
Silverlight does not support setting meta data options, so I cant think how the Telerik implementation will achieve that. It is possible that Telerik have their own DP implementation, or even that this type of property meta data only works with their controls.
Try using the standard System.Windows.PropertyMetaData type instead and see if that works for you.

Binding to user control's collection property items in xaml

I have created a user control with collection property:
public static readonly DependencyProperty
MyListProperty = DependencyProperty.Register(
"MyList",
typeof(ObservableCollection<Test>),
typeof(UserControl1),
new FrameworkPropertyMetadata(new ObservableCollection<Test>())
);
public ObservableCollection<Test> MyList
{
get { return (ObservableCollection<Test>)base.GetValue(MyListProperty); }
set { base.SetValue(MyListProperty, value); }
}
public static readonly DependencyProperty
BProperty = DependencyProperty.Register(
"B",
typeof(string),
typeof(UserControl1),
new FrameworkPropertyMetadata(null)
);
public string B
{
get { return (string)base.GetValue(BProperty); }
set { base.SetValue(BProperty, value); }
}
The Test class is:
public class Test : DependencyObject
{
public static readonly DependencyProperty
AProperty = DependencyProperty.Register(
"A",
typeof(string),
typeof(Test),
new FrameworkPropertyMetadata(null)
);
public string A
{
get { return (string)base.GetValue(AProperty); }
set { base.SetValue(AProperty, value); }
}
}
Then, i'm trying to use my control for binding:
<TextBox x:Name="tb1" Text="def"/>
<my:UserControl1 x:Name="uc1" B="{Binding ElementName=tb1, Path=Text}">
<my:UserControl1.MyList>
<my:Test A="{Binding ElementName=tb1, Path=Text}"></my:Test>
<my:Test A="100"></my:Test>
</my:UserControl1.MyList>
</my:UserControl1>
The first binding (with B property of User Control) works correctly. The problem is with second binding (with A property of Test which is MyList element). When debugging i have two items in MyList but the A property of the first one is null. Please tell me what I am missing here?
The problem here is, that the Binding to ElementName=tb1 can not be resolved, even it will never be evaluated. A Binding to an ElementName is resolved for DependencyObjects which are in the visual or logical Tree of a WPF Application. Adding items to your ObservableCollection (MyList) only means adding the items to the Collection, but not into the Visual Tree.
Edit:
Here is the approach discussed in the comments:
In your Window/Page:
<Window.Resources>
<!-- Declare the ViewModel as Resource -->
<my:ViewModel x:Key="viewModel">
<my:ViewModel.MyList>
<my:Test A="Hello sweet" />
<my:Test A="ViewModel" />
</my:ViewModel.MyList>
</my:ViewModel>
</Window.Resources>
<!-- Assign Ressource as DataContext -->
<StackPanel DataContext="{StaticResource viewModel}">
<TextBox x:Name="tb1" Text="def"/>
<!-- Reference your list within the ViewModel -->
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- Bind your property -->
<TextBlock Text="{Binding Path=A}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
And the implementation of ViewModel:
public class ViewModel
{
public ViewModel()
{
this.MyList = new ObservableCollection<Test>();
}
public ObservableCollection<Test> MyList { get; set; }
}
Of course, class Test no longer needs to implement a DependencyObject. Simple get/set Properties are okay.

Assign binding path at runtime in WPF DataTemplate

I am writing a WPF program in C# in which I have a ListView for which the columns will be populated at runtime. I would like to use a custom DataTemplate for the GridViewColumn objects in the ListView.
In the examples I have seen where the number of columns is fixed in advance, a custom DataTemplate is often created using something like the XAML below.
<DataTemplate x:Key="someKey">
<TextBlock Text="{Binding Path=FirstName}" />
</DataTemplate>
This DataTemplate could also later be assigned to GridViewColumn.CellTemplate in the code-behind by calling FindResource("someKey"). However, this alone is of no use to me, because in this example the Path element is fixed to FirstName. Really I need something where I can set the Path in code.
It is my impression that something along these lines may be possible if XamlReader is used, but I'm not sure how in practice I would do this. Any solutions are greatly appreciated.
It is easy to build what you need using two DataTemplates working in concert: The outer DataTemplate simply sets the DataContext for the inner DataTemplate, as follows:
<DataTemplate x:Key="DisplayTemplate">
<Border ...>
<TextBlock Text="{Binding}" ... />
</Border>
</DataTemplate>
<DataTemplate x:Key="CellTemplate">
<ContentPresenter Content="{Binding FirstName}"
ContentTemplate="{StaticResource DisplayTemplate}" />
</DataTemplate>
The only tricky thing is making it convenient to set this on a GridViewColumn. I would accomplish this with attached properties, allowing you to write:
<GridViewColumn
my:GVCHelper.DisplayPath="FirstName"
my:GVCHelper.Template="{StaticResource DisplayTemplate}" />
Or equivalently in code:
var col = new GridViewColumn();
GVCHelper.SetDisplayPath(col, "FirstName");
GVCHelper.SetTemplate(col, (DataTemplate)FindResource("DisplayTemplate"));
Either of these would cause the DataTemplate named "DisplayTemplate" to be used to display the FirstName in the column.
The helper class would be implemented as:
public class GVCHelper : DependencyObject
{
public static string GetDisplayPath(DependencyObject obj) { return (string)obj.GetValue(DisplayPathProperty); }
public static void SetDisplayPath(DependencyObject obj, string value) { obj.SetValue(DisplayPathProperty, value); }
public static readonly DependencyProperty DisplayPathProperty = DependencyProperty.RegisterAttached("DisplayPath", typeof(string), typeof(GVCHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) => Update(obj)
});
public static DataTemplate GetTemplate(DependencyObject obj) { return (DataTemplate)obj.GetValue(TemplateProperty); }
public static void SetTemplate(DependencyObject obj, DataTemplate value) { obj.SetValue(TemplateProperty, value); }
public static readonly DependencyProperty TemplateProperty = DependencyProperty.RegisterAttached("Template", typeof(DataTemplate), typeof(GVCHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) => Update(obj)
});
private static void Update(DependencyObject obj)
{
var path = GetDisplayPath(obj);
var template = GetTemplate(obj);
if(path!=null && template!=null)
{
var factory = new FrameworkElementFactory(typeof(ContentPresenter));
factory.SetBinding(ContentPresenter.ContentProperty, new Binding(path));
factory.SetValue(ContentPresenter.ContentTemplateProperty, template);
obj.SetValue(GridViewColumn.CellTemplateProperty,
new DataTemplate { VisualTree = factory };
}
}
}
How it works: Whenever the properties are both set, a new DataTemplate is constructed and the GridViewColumn.CellTemplate property is updated.
Maybe GridViewColumn.CellTemplateSelector would help you? Or you could create a user control, bind it to something big enough (that's the same for each column). Then let this control figure out what exactly it should display based on what it has in DataContext...

How to bind to a WPF dependency property when the datacontext of the page is used for other bindings?

How to bind to a WPF dependency property when the datacontext of the page is used for other bindings? (Simple question)
The datacontext of the element needed to be set.
XAML:
<Window x:Class="WpfDependencyPropertyTest.Window1" x:Name="mywindow">
<StackPanel>
<Label Content="{Binding Path=Test, ElementName=mywindow}" />
</StackPanel>
</Window>
C#:
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(string),
typeof(Window1),
new FrameworkPropertyMetadata("Test"));
public string Test
{
get { return (string)this.GetValue(Window1.TestProperty); }
set { this.SetValue(Window1.TestProperty, value); }
}
Also see this related question:
WPF DependencyProperties
In XAML:
Something="{Binding SomethingElse, ElementName=SomeElement}"
In code:
BindingOperations.SetBinding(obj, SomeClass.SomethingProperty, new Binding {
Path = new PropertyPath(SomeElementType.SomethingElseProperty), /* the UI property */
Source = SomeElement /* the UI object */
});
Though usually you will do this the other way round and bind the UI property to the custom dependency property.

Resources