Difference Source and Datacontext in wpf - wpf

DataContext and Source seem to be very similar to me.
What are the advantages and disadvantages?
When to use which one?
With Source:
<TextBlock Text="{Binding Name, Source={StaticResource Person}}" />
Or the solution with DataContext:
public partial class DataContextSample : Window
{
public string Name {get; set;}
public DataContextSample()
{
InitializeComponent();
this.DataContext = this;
}
}
<TextBlock Text="{Binding Name}" />

A binding with out a specified Source binds to the DataContext property of the element.
The DataContext is a special property which, if not set, is redirected to the element's parent's DataContext. This prevents duplicate xaml (always setting the Source in every binding) and makes all bindings relative so it is easier to change the UI without having to adjust all Sources in the bindings.

Related

WPF DataTemplate with Binding

A WPF ComboBox/ListBox itemtemplate/datatemplate question please.
Let's say that I set DisplayMember="Name", this is equivalent to
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
"Name" is a field in my view model.
Now, if DisplayMember="{Binding Name}", "Name" is no longer a property in my view model but instead contains the name of the property in my view model that I want to display. Using an ItemTemplate, how would I set this up? Thank you in advance.
DisplayMemberPath is a property of the ItemsControl. And its binding path will be resolved relative to the ItemsControl's DataContext, not its items.
Example (pseudo code without INPC interface implementation):
public class ViewModel
{
// This sets the string name of the property (or the path to it)
// to be displayed in the items.
public string DisplayNameProperty {get; set;} = "Name" // Or = "Surname"
// Items Source
public IEnumerable Items {get; set;}
}
<ItemsControl ItemsSource="{Binding Items}"
DisplayMemberPath="{Binding DisplayNameProperty}"/>
The binding in the DisplayMemberPath property will allow you to control from the ViewModel which property will be displayed in ItemsControl's items.
But in practice, such a task is extremely rare.
Personally, in my practice, such a need has never arisen.

Using a variable in XAML binding expression

I'm building a control that can edit POCOs. There is a descriptor collection for the fields within the POCO that need to be edited and I'm binding a ListBox's ItemsSource to this collection. Amongst other things, the descriptor gives me the ability to select a suitable DataTemplate and the variable name in the POCO that this ListBox item should edit.
My ListBox is built like this:
<ListBox ItemsSource="{Binding ColumnCollection, ElementName=root}">
<ListBox.Resources>
<DataTemplate x:Key="TextTemplate">
<StackPanel>
<TextBlock Text="{Binding DisplayName}" />
<!-- !!! Question about following line !!! -->
<TextBox Text="{Binding ElementName=vm.CurentEditing, Path=PathName}" />
</StackPanel>
</DataTemplate>
<!-- Details omitted for brevity -->
<DataTemplate x:Key="PickListTemplate" />
<DataTemplate x:Key="BooleanTemplate" />
</ListBox.Resources>
<ListBox.ItemTemplateSelector>
<local:DataTypeSelector
TextTemplate="{StaticResource TextTemplate}"
PickListTemplate="{StaticResource PickListTemplate}"
BooleanTemplate="{StaticResource BooleanTemplate}"
/>
</ListBox.ItemTemplateSelector>
</ListBox>
It is the TextBox binding expression in the "TextTemplate" that I am having problems with. The problem is that "PathName" should not be taken as a literal string, but is the name of a string property in the ColumnDescription class (the collection type of ColumnCollection used for ListBox.ItemsSource), which gives the name of the POCO property I want to bind to (the POCO is "vm.CurrentEditing").
Is there some way to use the value of a property in XAML as input to a binding expression, or will I have to resort to code behind?
(Incidentally, specifying the ElementName as "x.y" as I have done above also seems to be invalid. I assume the "y" part should be in Path but that's currently taken up with my property name...!)
So you want to bind TextBox.Text to Property X of Object Y, where X and Y both change at runtime.
It sounds like what you want to do is something analogous to ListBox.DisplayMemberPath: You can bind a string or PropertyPath property to DisplayMemberPath and it'll work. The way I've done stuff like that is to have a dependency property of type String or PropertyPath, and programatically create a binding from that to whatever property.
So, I wrote an attached property which creates a binding.
public class POCOWrangler
{
#region POCOWrangler.BindPropertyToText Attached Property
public static String GetBindPropertyToText(TextBox obj)
{
return (String)obj.GetValue(BindPropertyToTextProperty);
}
public static void SetBindPropertyToText(TextBox obj, PropertyPath value)
{
obj.SetValue(BindPropertyToTextProperty, value);
}
public static readonly DependencyProperty BindPropertyToTextProperty =
DependencyProperty.RegisterAttached("BindPropertyToText", typeof(String), typeof(POCOWrangler),
new PropertyMetadata(null, BindPropertyToText_PropertyChanged));
private static void BindPropertyToText_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is String && d is TextBox)
{
var tb = d as TextBox;
var binding = new Binding((String)e.NewValue);
// The POCO object we're editing must be the DataContext of the TextBox,
// which is what you've got already -- but don't set Source explicitly
// here. Leave it alone and Binding.Source will be updated as
// TextBox.DataContext changes. If you set it explicitly here, it's
// carved in stone. That's especially a problem if this attached
// property gets initialized before DataContext.
//binding.Source = tb.DataContext;
binding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(tb, TextBox.TextProperty, binding);
}
}
#endregion POCOWrangler.BindPropertyToText Attached Property
}
And I wrote a quick example thing: There's a little class named Foo that has a Name property, and a viewmodel with two properties, Foo Foo and String DisplayPathName. It works! Of course, this depends on default TextBox editing behavior for whatever type the property happens to be. I think that will get you the same results as if you'd bound explicitly in XAML, but it sitll won't always necessarily be just what you want. But you could very easily go a little nuts and add some triggers in the DataTemplate to swap in different editors, or write a DataTemplateSelector.
I stuffed ViewModel.Foo in a ContentControl just to get a DataTemplate into the act, so that the TextBox gets his DataContext in the same manner as yours.
Note also that I'm getting DisplayPathName by a relative source from something outside the DataContext object -- it's not a member of Foo, of course, it's a member of the viewmodel.
C#
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel {
DisplayPathName = "Name",
Foo = new Foo { Name = "Aloysius" }
};
}
XAML
<ContentControl
Content="{Binding Foo}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox
local:POCOWrangler.BindPropertyToText="{Binding
DataContext.DisplayPathName,
RelativeSource={RelativeSource AncestorType=ContentControl}}"
/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
That was fun.

Silverlight Control Binding

Is there a way to bind a Silverlight control to an object (or database table's row) which contains the values of several control's properties, doing so without by define the binding for each property?
For instance:
Let's say I have the class (or entity based on database table's row) with the following values:
class TextBlockValues
{
public string Text{get; set;}
public string HorizontalAlignment{get; set;}
public string VerticalAlignment{get; set;}
}
I want to bind it to a TextBlock in my silverlight application (again without explicit specify the binding for each property).
Thank you for your time.
There are two parts in a binding: DataContext and the actual Binding objects. Once you set up the data context for an item, all the properties, and children will automatically use that.
For example:
<TextBlock Name="CaptionText" Text="{Binding Text}" HorizontalAlignment="{Binding HorizontalAlignment}" Height="20" TextAlignment="Center" FontStretch="Expanded" FontSize="13" />
And in the .cs file:
CaptionText.DataContext = myObject;
If I understand your question right the answer is no. Even though you can set the control's DataContext you still have to bind which property in the control binds to what in the class.

How to bind a property in itemtemplate to a proptery in itemsource?

Consider this container:
public class ItemInfo : DependencyObject
{
public string Name { get; set; }
public ObservableCollection<SomeDataItem> DataValues { get; set; }
...
Dependency object registration and event handling
...
}
public class MyItemSource : ObservableCollection<ItemInfo>
{
...
}
Now, I wish to display this data in a listview where the control that displays the item is custom. For that, I'd set the MyItemSource to listview's ItemSource and define a ItemTemplate. However, it seems that I have no access to ItemInfo in the ItemTemplate. This is my XAML:
<Grid>
<ListBox ItemsSource="{StaticResource MyStaticDataSource}"
Grid.IsSharedSizeScope="True">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ItemInfoUserControl x:Name="itemInfoUserControl"
Name = "{Binding Name}" <--- this doesn't work
Data = "{Binding DataValues}" <--- this doesn't work
Width="300" Height="200"
Grid.Row="1">
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Instead of binding to ItemSource's ItemInfo, it binds to the ItemInfoUserControl properties, which is not what I wish it to do. Is there a way to bind properties in itemtemplate to a property in itemsource ? Or is there an alternative approach to what i'm ultimately trying to accomplish ?
Thanks!
Well, first of all you need to use proper binding syntax:
Name = "{Binding Name}"
and
Data = "{Binding DataValues}"
Instead of just "Name = "Binding Name"" and "Data = "Binding DataValues"". Note the addition of "{" and "}" around your binding expression.
This might be enough to solve your problem, as long as Name and DataValues are DependencyProperties in ItemInfoUserControl. If not, you'll need to implement them as DependencyProperties in order to be able to bind to them in XAML. See here for a good MSDN article on defining custom dependency properties.
Edit: Also, just noticed -- you're setting both x:Name and Name. From this article on MSDN:
If Name is available as a property on the class, Name and x:Name can be used interchangeably as attributes, but a parse exception will result if both are specified on the same element. If the XAML is markup compiled, the exception will occur on the markup compile, otherwise it occurs on load.
Remove x:Name="itemInfoUserControl" and see if that helps.

How to bind local variable in WPF

I have silverlight usercontrol. This contains Service Entity object. see below
public partial class MainPage : UserControl
{
public ServiceRef.tPage CurrentPage { get; set; }
...
}
I need to bind CurrentPage.Title to TextBox
My xaml is here
<TextBox Text="{Binding Path=CurrentPage.Title, RelativeSource={RelativeSource self}}"></TextBox>
But it is not work.
How to do it?
In order for that to work, you'll have to implement INotifyPropertyChanged on your class and raise the PropertyChanged event for CurrentPage when it's set (this also means you won't be able to use auto properties; you'll have to use your own private instance backing variable and code the get { } and set { } yourself).
What's happening is the control is binding to the value before you've set CurrentPage. Because you aren't notifying anyone that the property has changed, it does not know to refresh the bound data. Implementing INotifyPropertyChanged will fix this.
Or you could just manually set the Text property yourself in the setter.
Change your markup to
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=CurrentPage.Title}" />
By assigning RelativeSource={RelativeSource self} your are telling the TextBlock to bind to itself and look for a property named CurrentPage on the TextBlock itself and not the parent Window.
set the UpdateSourceTrigger="PropertyChanged" in the XAML.

Resources