Why to use Path in silverlight databinding? - silverlight

I am new to the silverlight. I found some articles on the internet for databinding. I can see that binding is performed in different ways as follows
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Path=Name}"/>
<TextBlock Text="{Binding Path=Account}"/>
<TextBlock Text="{Binding Path=Property1.Property2.Property3}"/>
In the first textblock binding is performed with property name. In second example also binding is performed with property name. Then what is different with Path in second textblock ? I know how to do binding for the first three textblock but I am not aware how to do binding with the fourth textblock ? and when we use the binding of the following type
<TextBlock Text="{Binding Path=Property1.Property2.Property3}"/>
Can you please give me coding example along with explanation for all above thing ? If I misunderstood something then please guide me.

By using the following binding:
<TextBlock Text="{Binding Path=Property1.Property2.Property3}"/>
You're saying that the data context of the TextBlock has a property called Property1, which returns an object that has a property called Property2, which in turn has a property called Property3. The value returned by Property3 is what will be shown in the TextBox.
For example, if your classes looked like this and the data context for the TextBox was an instance of Foo, you'd see "Hello World" displayed:
public class Foo
{
public Foo1 Property1 { get; set; }
}
public class Foo1
{
public Foo2 Property2 { get; set; }
}
public class Foo2
{
public string Property3 { get { return "Hello World"; } }
}
The "." syntax just lets you refer to "subproperties" of an object. For more information, take a look at this MSDN article.

There is no difference between the first and the second TextBlock in your question.
Path is the 'default' property of the Binding object that gets set.

Related

Difference Source and Datacontext in 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.

DataTemplateSelector and its weird behavior

I am using a DataTemplateSelector inside a ContentControl. I have 3 different DataTemplates based on 3 different object types. When I set the content of my ContentControl to data of the mentioned types, the DataTemplateSelector swaps to the specific DataTemplate AND the selector futhermore seems to rollback/reset the values from the old template. Why is that so?
Edit: I figured out that the values get resetted because I have an attached property caled Prop and inside its OnPropertyChangedCallback it notifies me about the Prop having value null on swapping between DataTemplates. You can see that attached property in code below.
Can somebody help me out what happens behind this swapping mechanism of DataTemplateSelector?
Here is a deeper explaination with code:
public void Window1()
{
InitalizeComponents();
}
public void OnClick(object sender, RoutedEventArgs e)
{
if(this.DataContext == null)
this.DataContext = "Hallo";
else{
if(this.DataContext is string)
this.DataContext = 123;
else{
if(this.DataContext is int)
this.DataContext = null;
}
}
}
By clicking on Button few times I change the type and so in ContentControl the selector changes to DataTemplate.
The selector looks like this below. It swaps between textDataTemplate and numericDataTemplate and one more template. As I mentioned i have those three type which are string, int, and one more, that i wish not to metion. Their DataTemplates are called textDataTemplate, numericDataTemplate and that one more. :)
<local:MyTemplateSelector x:Key="dataTemplateSelector"
TextTemplate="{StaticResource textDataTemplate}"
NumericTemplate="{StaticResource numericDataTemplate}"/>
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate TextTemplate;
public DataTemplate NumericTemplate;
public DataTemplate Select(object item, Culture.....)
{
if(item is string)
{
return this.TextTemplate;
}
else
{
return this.NumericTemplate;
}
}
}
ContentControl and XAML looks like this:
<Button Click="OnClick" Content="Click Me"/>
<ContentControl Name="contentCtrl"
Content="{Binding}"
Width="123"
ContentTemplateSelector="{StaticResource dataTemplateSelector}" />
And this is how textDataTemplate looks alike.
<DataTemplate x:Key="textDataTemplate">
<TextBox x:Name="text" my:AttProperties.Prop="{extension:MarkupExt value}" Text="{Binding Path=Txt, Mode=Default, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
numericDataTemplate looks similar to textDataTemplate just that only digits are allowed.
The Prop is my attached property from AttProperties class of type string. The Prop is somewhere inside of all three DataTemplate. Above the Prop is sitting on a TextBox but it could be a Label too. The markupextension is just a "return Hello". The extension is just there to test how to create a custom markupextension. There is no big deal with the extension. It shouldnt have to do much with the swapping of DataTemplates.
One more time to explain my problem. Swapping seems reselts/rollback my old templates. I swap from textDataTemplate to lets say numericDataTemplate and the Prop of textDataTemplate gets set to null but the value before was "Hello".
Why is that happening? It seems like the same behavior with using tiggers. Once a Trigger is no more valid it rollsback the used values. Is a DataTemplateSelector using some kind of same mechanism as Triggers?
Edited:
The attached property is just a simple .RegisterAttached with an OnPropertyChangedCallback. Inside OnPropertyChangedCallback I figured the prop is null when swapping the dataTemplates.
If you use two-way binding in numeric template and it only accepts something like Double, it can set value to number. But no one can be sure without seeing full code. It's possible that your own code does something wrong.
To understand things better, create your own control, derived from the ContentControl, and use it in your sample. Then override control methods OnContentxxxChanged, insert breakpoints there and debug your application. You should understand, what's going on with your data and with template selector. When application stops on breakpoint, carefully check all values and look at stack trace. To debug bindings you can insert IValueConverters, it would give you place in code, where you can check values.
I really suggest you to make the simplest working thing first, and then go to more complicated things such as textboxes with two-way bindings to some property of some control which you didn't show in your question. Here is a working version with TextBlocks:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void OnClick(object sender, RoutedEventArgs e)
{
if (this.DataContext == null)
this.DataContext = "Hallo";
else if (this.DataContext is string)
this.DataContext = 123;
else if (this.DataContext is int)
this.DataContext = null;
}
}
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate TextTemplate {get; set;}
public DataTemplate NumericTemplate {get; set;}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is string)
{
return this.TextTemplate;
}
else
{
return this.NumericTemplate;
}
}
}
and xaml:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="numericDataTemplate">
<TextBlock Foreground="Red" Text="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="textDataTemplate">
<TextBlock Foreground="Green" Text="{Binding}"/>
</DataTemplate>
<local:MyTemplateSelector x:Key="dataTemplateSelector"
TextTemplate="{StaticResource textDataTemplate}"
NumericTemplate="{StaticResource numericDataTemplate}"/>
</Grid.Resources>
<StackPanel>
<Button Click="OnClick" Content="Click Me" VerticalAlignment="Top"/>
<ContentControl Name="contentCtrl"
Content="{Binding}"
Width="300" Height="100"
ContentTemplateSelector="{StaticResource dataTemplateSelector}" />
</StackPanel>
</Grid>
Compare with your code. When you inherit from DataTemplateSelector, you should override SelectTemplate method and don't invent methods with other names. All controls such as ContentControl will only use SelectTemplate. Etc..
Obviously, all works and DataTemplateSelector does nothing wrong. I suppose, your problem is somewhere in your data and bindings
And look at your OnClick method - it always sets DataContext to null

Make only one of many controls visible based on value of another control

I have a ComboBox bound to a collection of objects defined as this.
public class TierOption
{
public string Option { get; set; }
public Type DataType { get; set; }
}
public class TierOptions : ObservableCollection<Tier1Option>
{
}
I have 3 other controls related to this ComboBox, which are a TextBox, ComboBox, or a WPFToolKit:DatePicker.
I need to show only the related control which corresponds to the datatype(Type) of the object selected in the first ComboBox and neither of the others.
Pseudo Code Example:
(Probably too close to butchered C# but hopefully it conveys the idea)
switch (ComboBox.SelectedItem.DataType)
{
case String:
TextBox.Visibility = Visibility.Visible;
ComboBox.Visibility = Visibility.Hidden;
DatePicker.Visibility = Visibility. Hidden;
break;
case DateTime:
TextBox.Visibility = Visibility.Hidden;
ComboBox.Visibility = Visibility.Hidden;
DatePicker.Visibility = Visibility. Visible;
break;
<...so forth and so on...>
}
My attempts have resulted in very non-wpf looking convoluted messes which don't work regardless. Being new to wpf I'm trying very hard to stay true to the best design practices.
Thank you!
You can play with DataTemplate with DataType property
<...Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type DateTime}">
<DatePicker .../>
</DataTemplate>
...
</...Resources>
<ContentControl Content="{Binding SelectedItem, ElementName=myComboBox}"/>
<ComboBox ItemsSource="{Binding ...}"/>
The code above is just the idea, you could have to make adjustements. For example you won't be able to modify a string item itself (you could have to encapsulate each item of your list)
If your list contains all items of the same type, you can use a ContentTemplateSelector on contentControl.
ContentControl Content="{Binding SelectedItem, ElementName=YourCombBox}" ContentTemplateSelector="{StaticResource YourTemplateSelector}"
MSDN DOC about ContentControl.ContentTemplateSelector Property
Bind to the detail visibility to ElementName=ComboBox Path=SelectedItem.DataType. And you will need to use a converter that returns visibility. You will need two converters return opposite answers. If you have more than 2 combination then some more in the line of Jonas.
I assumed Type was a system class and it appears to be a custom class. Extend that class to have additional properties. Even if Type was a system type you could just create a class that implements it and extend it.
public Visibility TextBoxVisibility { get; }
public Visibility ComboBoxVisibility { get; }
...
Then on TextBox bind the visibility
Visisbility="{binding ElementName=Combobox Path=SelectedItem.DataType.TextBoxVisibility]";

Setter but not for Style in WPF?

I know how Triggers with Setters work in WPF, and I know that Setters can only change Style properties. Is there something equivalent to the Setter for non-Style properties? I really want to be able to change a property on custom object that is instantiated in XAML. Any ideas?
Edit: While Setters can update any dependency property, I am attempting to do this within an EventTrigger, which I forgot to specify. There is this workaround, but I'm not sure if it is really best practice. It uses storyboards and ObjectAnimationUsingKeyFrames. Is there anything wrong with this?
Using Interactivity from the Blend SDK you can do this in XAML, you only need to create a TriggerAction which sets the property.
Edit: There already is such an action in another namespace: ChangePropertyAction
In XAML you can use this namespace: http://schemas.microsoft.com/expression/2010/interactions
Tested example:
public class PropertySetterAction : TriggerAction<Button>
{
public object Target { get; set; }
public string Property { get; set; }
public object Value { get; set; }
protected override void Invoke(object parameter)
{
Type type = Target.GetType();
var propertyInfo = type.GetProperty(Property);
propertyInfo.SetValue(Target, Value, null);
}
}
<StackPanel>
<StackPanel.Resources>
<obj:Employee x:Key="myEmp" Name="Steve" Occupation="Programmer"/>
</StackPanel.Resources>
<TextBlock>
<Run Text="{Binding Source={StaticResource myEmp}, Path=Name}"/>
<Run Name="RunChan" Text=" - "/>
<Run Text="{Binding Source={StaticResource myEmp}, Path=Occupation}"/>
</TextBlock>
<Button Content="Demote">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<t:PropertySetterAction Target="{StaticResource myEmp}"
Property="Occupation"
Value="Coffee Getter"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
Note that with Value being an object default ValueConversion will not take place, if you enter a value as an attribute (Value="Something") it will be interpreted as a string. To set an int for example you can do this:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<t:PropertySetterAction Target="{StaticResource myEmp}"
Property="Id">
<t:PropertySetterAction.Value>
<sys:Int32>42</sys:Int32>
</t:PropertySetterAction.Value>
</t:PropertySetterAction>
Have you declared the properties that you want to set as dependency properties? I can't find the project I did this in, but I am pretty sure that is what fixed it for me.
I tried implementing something pretty simple, and got the following:
The property "Type" is not a DependancyProperty. To be used in markup, non-attached properties must be exposed on the target type with and accessible instance property "Type". For attached properties the declaring type must provide static "GetType" and "SetType" methods.
Here is a dependancy property registration example from another project of mine:
Public Shared TitleProperty As DependencyProperty = DependencyProperty.Register("Title", GetType(String), GetType(SnazzyShippingNavigationButton))
In the above example, SnazzyShippingNavigationButton is the Class Name of which the property is a member.
And the associated property declaration:
<Description("Title to display"), _
Category("Custom")> _
Public Property Title() As String
Get
Return CType(GetValue(TitleProperty), String)
End Get
Set(ByVal value As String)
SetValue(TitleProperty, value)
End Set
End Property
Description and Category attributes only really apply to the IDE designer property grid display.

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.

Resources